什么时候应该在C#中使用结构而不是类?我的概念模型是,当项只是值类型的集合时,使用结构。一种将它们逻辑地结合在一起的方法。
我在这里遇到了这些规则:
结构应表示单个价值结构应具有内存占用空间小于16字节。结构不应在之后更改创造
这些规则有效吗?结构在语义上意味着什么?
什么时候应该在C#中使用结构而不是类?我的概念模型是,当项只是值类型的集合时,使用结构。一种将它们逻辑地结合在一起的方法。
我在这里遇到了这些规则:
结构应表示单个价值结构应具有内存占用空间小于16字节。结构不应在之后更改创造
这些规则有效吗?结构在语义上意味着什么?
当前回答
我使用结构来打包或解包任何类型的二进制通信格式。这包括读取或写入磁盘、DirectX顶点列表、网络协议或处理加密/压缩数据。
在这方面,你列出的三条准则对我来说并不有用。当我需要以特殊顺序写出400字节的内容时,我将定义一个400字节的结构,并用它应该具有的任何不相关的值填充它,我将以当时最合理的方式设置它。(好吧,四百字节会很奇怪——但当我以写Excel文件为生的时候,我处理的是多达四十字节的结构,因为这就是一些BIFF记录的大小。)
其他回答
.NET支持值类型和引用类型(在Java中,只能定义引用类型)。引用类型的实例在托管堆中分配,并且在没有未完成的引用时被垃圾收集。另一方面,值类型的实例是在堆栈中分配的,因此一旦其作用域结束,分配的内存就会被回收。当然,值类型通过值传递,引用类型通过引用传递。除了System.String之外,所有C#原语数据类型都是值类型。
何时在类上使用结构,
在C#中,结构是值类型,类是引用类型。您可以使用enum关键字和struct关键字在C#中创建值类型。使用值类型而不是引用类型将导致托管堆上的对象减少,从而减少垃圾收集器(GC)的负载,减少GC周期,从而提高性能。然而,价值类型也有其缺点。传递一个大结构肯定比传递一个引用成本更高,这是一个明显的问题。另一个问题是与装箱/拆箱相关的开销。如果您想知道装箱/拆箱是什么意思,请按照以下链接了解装箱和拆箱的详细说明。除了性能之外,有时您只需要类型具有值语义,如果引用类型是您的全部,那么这将很难实现(或者很难看)。当您需要复制语义或需要自动初始化时(通常在这些类型的数组中),您应该只使用值类型。
结构适合于数据的原子表示,其中所述数据可以被代码复制多次。克隆对象通常比复制结构更昂贵,因为它涉及分配内存、运行构造函数以及在完成时释放/垃圾回收。
以下是在Microsoft网站上定义的规则:
✔️ 如果类型的实例很小且通常很短,或者通常嵌入在其他对象中,请考虑定义结构而不是类。
❌ 避免定义结构,除非该类型具有以下所有特征:
它在逻辑上表示单个值,类似于原始类型(int、double等)。
它的实例大小小于16字节。
它是不可变的。
它不必经常装箱。
供进一步阅读
根据C#语言规范:
1.7结构与类一样,结构是可以包含数据成员和函数成员的数据结构,但与类不同,结构是值类型,不需要堆分配。结构的变量类型直接存储结构的数据,而类类型存储对动态分配对象的引用。结构类型不支持用户指定的继承,并且所有结构类型隐式继承自类型对象。结构对于具有值语义。复数、坐标系中的点或字典中的键值对都是结构的好例子。这个对小数据结构使用结构而不是类可以应用程序内存分配数量的巨大差异执行。例如,以下程序创建并初始化100个点的阵列。将Point实现为类,101单独的对象被实例化,一个用于数组,另一个用于100个元素。
class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
}
}
另一种方法是使Point成为结构。
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
现在,只实例化了一个对象,即数组的对象,Point实例存储在数组中。
结构构造函数是用新运算符调用的,但这并不意味着正在分配内存。结构构造函数不是动态分配对象并返回对它的引用,而是简单地返回结构值本身(通常在堆栈上的临时位置),然后根据需要复制该值。
使用类,两个变量可以引用同一个对象,因此对一个变量的操作可能会影响另一个变量引用的对象。对于结构,每个变量都有自己的数据副本,对其中一个变量的操作不可能影响另一个变量。例如,以下代码片段产生的输出取决于Point是类还是结构。
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
如果Point是一个类,则输出为20,因为a和b引用相同的对象。如果Point是一个结构,则输出为10,因为将a赋值给b会创建一个值的副本,并且该副本不受后续对a.x赋值的影响。
上一个示例突出了结构的两个限制。首先,复制整个结构通常比复制对象引用效率低,因此与引用类型相比,结构的赋值和值参数传递可能更昂贵。第二,除了ref和out参数之外,不可能创建对结构的引用,这在许多情况下排除了它们的使用。
我刚刚在处理Windows Communication Foundation[WCF]命名管道,我注意到使用Structs确实有意义,以确保数据交换是值类型而不是引用类型。