.NET中的结构和类有什么区别?
当前回答
结构是实际值-它们可以为空,但不能为空
这是正确的,但是也要注意,从.NET2开始,结构支持Nullable版本,C#提供了一些语法糖,使其更易于使用。
int? value = null;
value = 1;
其他回答
有一个有趣的“类vs结构”难题案例——当您需要从方法返回几个结果时:选择要使用的结果。如果你知道ValueTuple的故事,你就知道添加ValueTuple(结构)是因为它应该比Tuple(类)更有效。但这在数字上意味着什么?两个测试:一个是具有2个字段的结构/类,另一个是有8个字段的类型/类(维度大于4-从处理器节拍的角度来看,类应该比结构更有效,但当然也应该考虑GC负载)。
P.S.另一个特定案例“stuct or class with collections”的基准是:https://stackoverflow.com/a/45276657/506147
BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
[Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Clr : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
Core : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Method | Job | Runtime | Mean | Error | StdDev | Min | Max | Median | Rank | Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
TestStructReturn | Clr | Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns | 4 | 0.0127 | 40 B |
TestClassReturn | Clr | Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns | 5 | 0.0229 | 72 B |
TestStructReturn8 | Clr | Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns | 8 | 0.0127 | 40 B |
TestClassReturn8 | Clr | Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns | 6 | 0.0305 | 96 B |
TestStructReturn | Core | Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns | 1 | 0.0127 | 40 B |
TestClassReturn | Core | Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns | 2 | 0.0229 | 72 B |
TestStructReturn8 | Core | Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns | 7 | 0.0127 | 40 B |
TestClassReturn8 | Core | Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns | 3 | 0.0305 | 96 B |
代码测试:
using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;
namespace Benchmark
{
//[Config(typeof(MyManualConfig))]
[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkStructOrClass
{
static TestStruct testStruct = new TestStruct();
static TestClass testClass = new TestClass();
static TestStruct8 testStruct8 = new TestStruct8();
static TestClass8 testClass8 = new TestClass8();
[Benchmark]
public void TestStructReturn()
{
testStruct.TestMethod();
}
[Benchmark]
public void TestClassReturn()
{
testClass.TestMethod();
}
[Benchmark]
public void TestStructReturn8()
{
testStruct8.TestMethod();
}
[Benchmark]
public void TestClassReturn8()
{
testClass8.TestMethod();
}
public class TestStruct
{
public int Number = 5;
public struct StructType<T>
{
public T Instance;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance;
}
private StructType<int> Method1(int i)
{
return Method2(++i);
}
private StructType<int> Method2(int i)
{
return Method3(++i);
}
private StructType<int> Method3(int i)
{
return Method4(++i);
}
private StructType<int> Method4(int i)
{
var x = new StructType<int>();
x.List = new List<string>();
x.Instance = ++i;
return x;
}
}
public class TestClass
{
public int Number = 5;
public class ClassType<T>
{
public T Instance;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance;
}
private ClassType<int> Method1(int i)
{
return Method2(++i);
}
private ClassType<int> Method2(int i)
{
return Method3(++i);
}
private ClassType<int> Method3(int i)
{
return Method4(++i);
}
private ClassType<int> Method4(int i)
{
var x = new ClassType<int>();
x.List = new List<string>();
x.Instance = ++i;
return x;
}
}
public class TestStruct8
{
public int Number = 5;
public struct StructType<T>
{
public T Instance1;
public T Instance2;
public T Instance3;
public T Instance4;
public T Instance5;
public T Instance6;
public T Instance7;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance1;
}
private StructType<int> Method1(int i)
{
return Method2(++i);
}
private StructType<int> Method2(int i)
{
return Method3(++i);
}
private StructType<int> Method3(int i)
{
return Method4(++i);
}
private StructType<int> Method4(int i)
{
var x = new StructType<int>();
x.List = new List<string>();
x.Instance1 = ++i;
return x;
}
}
public class TestClass8
{
public int Number = 5;
public class ClassType<T>
{
public T Instance1;
public T Instance2;
public T Instance3;
public T Instance4;
public T Instance5;
public T Instance6;
public T Instance7;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance1;
}
private ClassType<int> Method1(int i)
{
return Method2(++i);
}
private ClassType<int> Method2(int i)
{
return Method3(++i);
}
private ClassType<int> Method3(int i)
{
return Method4(++i);
}
private ClassType<int> Method4(int i)
{
var x = new ClassType<int>();
x.List = new List<string>();
x.Instance1 = ++i;
return x;
}
}
}
}
结构是实际值-它们可以为空,但不能为空
这是正确的,但是也要注意,从.NET2开始,结构支持Nullable版本,C#提供了一些语法糖,使其更易于使用。
int? value = null;
value = 1;
除了其他答案中描述的所有差异外:
结构不能具有显式无参数构造函数,而类可以结构不能有析构函数,而类可以结构不能从其他结构或类继承,而类可以从其他类继承。(结构和类都可以从接口实现。)
如果您正在观看一段视频,解释所有的差异,您可以查看第29部分-C#教程-C#中类和结构之间的差异。
类中声明的事件通过锁(this)自动锁定其+=和-=访问,以使其线程安全(静态事件根据类的类型锁定)。结构中声明的事件没有自动锁定其+=和-=访问。结构的锁(this)无法工作,因为只能锁定引用类型表达式。创建结构实例不能导致垃圾收集(除非构造函数直接或间接创建引用类型实例),而创建引用类型的实例会导致垃圾收集。结构始终具有内置的公共默认构造函数。类DefaultConstructor{静态空隙Eg(){直接yes=新的直接();//始终编译正常InDirect可能=新InDirect();//如果构造函数存在且可访问,则编译//...}}这意味着结构总是可实例化的,而类可能不是,因为它的所有构造函数都可以是私有的。类不可实例化{private NonInstantiable()//确定{}}结构Direct{private Direct()//编译时错误{}}结构不能有析构函数。析构函数只是对象的重写。伪装完成,结构作为值类型,不受垃圾收集的影响。结构Direct{~Direct(){}//编译时错误}InDirect类{~InDirect(){}//编译正常}~Indirect()的CIL如下所示:.method家族hidebysig虚拟实例无效Finalize()cil已管理{// ...}//方法结束间接::Finalize结构是隐式密封的,类不是。结构不能是抽象的,类可以。结构不能在其构造函数中调用:base(),而没有显式基类的类可以。结构不能扩展另一个类,类可以。结构不能声明类可以声明的受保护成员(例如,字段、嵌套类型)。结构不能声明抽象函数成员,抽象类可以。结构不能声明虚函数成员,类可以。结构不能声明密封的函数成员,类可以。结构不能声明重写函数成员,类可以。此规则的一个例外是,结构可以重写System.Object的虚拟方法,即Equal()、GetHashCode()和ToString()。
Struct | Class | |
---|---|---|
Type | Value-type | Reference-type |
Where | On stack / Inline in containing type | On Heap |
Deallocation | Stack unwinds / containing type gets deallocated | Garbage Collected |
Arrays | Inline, elements are the actual instances of the value type | Out of line, elements are just references to instances of the reference type residing on the heap |
Al-Del Cost | Cheap allocation-deallocation | Expensive allocation-deallocation |
Memory usage | Boxed when cast to a reference type or one of the interfaces they implement, Unboxed when cast back to value type (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |
No boxing-unboxing |
Assignments | Copy entire data | Copy the reference |
Change to an instance | Does not affect any of its copies | Affect all references pointing to the instance |
Mutability | Should be immutable | Mutable |
Population | In some situations | Majority of types in a framework should be classes |
Lifetime | Short-lived | Long-lived |
Destructor | Cannot have | Can have |
Inheritance | Only from an interface | Full support |
Polymorphism | No | Yes |
Sealed | Yes | When have sealed keyword (C#), or Sealed attribute (F#) |
Constructor | Can not have explicit parameterless constructors | Any constructor |
Null-assignments | When marked with nullable question mark | Yes (When marked with nullable question mark in C# 8+ and F# 5+ 1) |
Abstract | No | When have abstract keyword (C#), or AbstractClass attribute (F#) |
Member Access Modifiers | public , private , internal |
public , protected , internal , protected internal , private protected |
1不鼓励在F#中使用null,请改用Option类型。
推荐文章
- 如何为构造函数定制Visual Studio的私有字段生成快捷方式?
- 为什么Visual Studio 2015/2017/2019测试运行器没有发现我的xUnit v2测试
- AppSettings从.config文件中获取值
- 如何检查IEnumerable是否为空或空?
- 没有ListBox。SelectionMode="None",是否有其他方法禁用列表框中的选择?
- 在c#代码中设置WPF文本框的背景颜色
- 如何在iis7应用程序池中设置。net Framework 4.5版本
- 如何分裂()一个分隔字符串到一个列表<字符串>
- 如何指定最小值,但没有使用范围数据注释属性的最大小数?
- 类中的Python装饰器
- 如何在PowerShell中获得本地主机名?
- 为什么在Java和。net中不能修改字符串?
- 'throw'和'throw new Exception()'的区别
- c# int到字节[]
- 如何跟踪log4net问题