C# 什么时候结构比类好?

C# 什么时候结构比类好?,c#,.net,class,structure,C#,.net,Class,Structure,副本: 在Microsoft.NET 2.0/3.5中使用结构而不是某些类是否有实际原因 “结构和类之间的区别是什么?”-这可能是IntrView for“.NET developer”中最流行的问题。面试官认为唯一正确的答案是“结构是在堆栈上分配的,类是在堆上分配的”,对此没有进一步的问题 一些谷歌搜索显示: a)结构有许多限制,与类和类相比没有额外的能力 b)堆栈(因此 结构)在非常特殊的条件下可以更快,包括: 数据块大小小于16字节 无大量装箱/拆箱 结构的成员几乎是不变的 整个数据集不

副本:

在Microsoft.NET 2.0/3.5中使用结构而不是某些类是否有实际原因


“结构和类之间的区别是什么?”-这可能是IntrView for“.NET developer”中最流行的问题。面试官认为唯一正确的答案是“结构是在堆栈上分配的,类是在堆上分配的”,对此没有进一步的问题

一些谷歌搜索显示:

a)结构有许多限制,与类和类相比没有额外的能力
b)堆栈(因此 结构)在非常特殊的条件下可以更快,包括:

  • 数据块大小小于16字节
  • 无大量装箱/拆箱
  • 结构的成员几乎是不变的
  • 整个数据集不是很大(否则会出现堆栈溢出)
(如果错误或不完整,请更正/添加到此列表)

据我所知,大多数典型的商业项目(ERM、会计、银行解决方案等)甚至都没有定义一个结构,所有自定义数据类型都定义为类。这种方法是否有错误或至少不完善之处

注意:问题是关于普通的商业应用程序,请不要列出“不寻常”的情况,如游戏开发、实时动画、向后兼容性(COM/Interop)、非托管代码等-这些答案已经在这个类似的问题下面了:


类的另一个区别是,当您将结构实例分配给变量时,您不仅复制引用,而且实际上复制整个结构。因此,如果您修改其中一个实例(无论如何都不应该修改,因为结构实例是不可变的),那么另一个实例就不会被修改。

IMO最重要的用例是小型组合实体的大型数组。想象一个包含10^6个复数的数组。或包含1000x1000个24位RGB值的二维阵列。在这种情况下,使用struct而不是类可以产生巨大的差异

编辑: 澄清一下:假设你有一个结构

struct RGB 
{
   public byte R,G,B;
}
如果声明一个包含1000x1000个RGB值的数组,该数组将占用3 MB的内存,因为这些值类型是内联存储的


如果使用类而不是结构,则数组将包含1000000个引用。仅此一项就需要4或8 MB(在64位机器上)内存。如果使用单独的对象初始化所有项,以便可以单独修改值,那么将有1000000个对象在托管堆上旋转,以保持GC繁忙。每个对象有2个引用的开销(IIRC),即对象将使用11/19MB的内存。总的来说,这是simple struct版本内存的5倍。

堆栈分配值类型的一个优点是它们是线程的本地类型。这意味着它们本质上是线程安全的。这对于堆上的对象来说是不可能的


当然,这假设我们谈论的是安全的托管代码。

有时候,您只想在组件之间传输数据,那么struct比class好。e、 g.仅携带数据的数据传输对象(DTO)

据我所知,大多数典型的商业项目(ERM、会计、银行解决方案等)甚至都没有定义一个结构,所有自定义数据类型都定义为类。这种方法是否有错误或至少不完善之处

不!这一切都是完全正确的。您的一般规则应该是默认情况下始终使用对象。毕竟,我们谈论面向对象编程是有原因的,而不是面向结构的编程(结构本身缺少一些OO原则,如继承和抽象)

然而,结构有时更好,如果:

  • 您需要精确控制所使用的内存量(结构使用的内存(取决于大小)比对象少一点到少很多
  • 您需要精确控制内存布局。这对于与Win32或其他本机API的互操作尤其重要
  • 您需要尽可能快的速度(在许多具有较大数据集的场景中,如果正确使用结构,您可以获得相当好的加速)
  • 你需要消耗更少的内存,并且在数组中有大量结构化数据。特别是与数组结合使用时,你可以通过结构节省大量内存
  • 您正在广泛地使用指针。然后结构提供了许多有趣的特性

到目前为止,所有的答案都很好……我只需要补充一点,根据定义,值类型是不可为空的,因此在您不想麻烦创建类的新实例并将其分配给字段的情况下,它是一个很好的候选值,例如

struct Aggregate1
{
    int A;
}

struct Aggregate2
{
    Aggregate1 A;
    Aggregate1 B;
}
注意:如果
Aggregate1
是一个类,则必须手动初始化Aggregate2中的字段

Aggregate2 ag2 = new Aggregate2();
ag2.A = new Aggregate1();
ag2.B = new Aggregate1();

只要Aggregate1是一个结构,这显然不是必需的……当您创建一个类/结构继承人权限以明确使用
XmlSerializer进行序列化/反序列化时,这可能会很有用。在这种情况下,许多看似神秘的异常将仅通过使用结构而消失。

一些非常好的答案涉及到使用结构与类以及visa与类的实用性,但我认为你最初关于结构不可变的评论是一个很好的论据,可以解释为什么在LOB应用程序的高级设计中更经常使用类。在域驱动设计中,实体/Cla之间存在某种程度的平行性SSE和值对象/结构。DDD中的实体是业务域中的项,我们需要使用标识符跟踪其标识,例如CustomerId、ProductId等。值对象是
static void Offset(ref it, double x, double y)
{
  it.dx += x;
  it.dy += y;
}
static void Offset(ref it, double x, double y)
{
  it = new Transform2d(it.xx, int.xy, it.yx, it.yy, it.dx+x, it.dy+y);
}
Transform2d Offset(double dx, double dy)
{
  it = new Transform2d(xx, xy, yx, yy, dx+x, dy+y);
}