C#中类和结构体的“开销”差异?

16

我正在学习课程3354(在.NET Framework 2.0中实现系统类型和接口),据说对于具有成员变量和函数的简单类而言,使用struct比使用class更好,因为能够减少开销。

我从未听说过这样的说法,这种说法是否有可靠性呢?

11个回答

13

我建议除非你有非常特定的用例并且知道结构体将如何使系统受益,否则不要使用结构体。

虽然C#结构体允许成员,但它们与类的工作方式有很大不同(不能被子类型化,没有虚拟分派,可能完全存在于堆栈上),并且行为会随着提升等因素而改变。(提升是将值类型提升到堆上的过程--惊喜!)

所以,回答这个问题:我认为C#中最大的误解之一就是使用结构体“提高性能”。原因是“开销”无法真正衡量,除非看到它如何与系统的其余部分交互以及它所扮演的角色(如果有任何值得注意的)。这需要进行分析,并且不能用“开销更小”的如此琐碎的陈述来概括。

对于结构体值类型,有一些好的情况--一个例子是存储在图像数组中的复合RGB值。这是因为RGB类型很小,在图像中可以有很多,值类型可以在数组中很好地打包,并且可能有助于保持更好的内存局部性等。


我猜你的意思是你从未定义过一个结构体。你可能每天都在使用它们,却没有意识到。 - Gabe
@Gabe 没错,我已经修复了 :-) - user166390
@pst:我认为Gabe指的是你说“我建议永远不要使用结构体”。显然这不是你的意思。 - Mark Byers
始终使用尽可能简单的数据结构。如果不需要,使用类/引用类型的好处是什么? - Jim Anderson
2
@Jim Anderson 为什么结构体比类“简单”?它的功能更有限,是的——但它如何更简单? - user166390

10
一般来说,出于性能原因,不应将应该是类的类型更改为结构体。结构体和类具有不同的语义,不能完全互换。虽然结构体有时可能更快,但它们有时也可能更慢。它们的行为不同,您应该知道何时使用其中之一。
MSDN有一篇关于选择类和结构体的页面。
总结如下:
不要定义一个结构体,除非该类型具有以下所有特征:
- 它在逻辑上表示单个值,类似于原始类型(整数、双精度等)。 - 它的实例大小小于16字节。 - 它是不可变的。 - 它不必经常装箱。
偶尔可能会有一种情况,某个类型确实应该是类,但由于性能原因需要将其作为结构体。这种情况很少见,您应该确保在采用此方法之前知道自己在做什么。如果违反结构体的不变性准则,尤其危险。可变结构体的行为可能超出大多数程序员的预期。

7
现在优化器支持内联结构体访问(至少在x86上),16字节的大小限制已不再适用。大约128字节左右可能是一个合适的新上限。 - Ben Voigt

9
当C#还处于beta版本时,我曾经看到过一个展示类和结构体区别的演示。开发人员构建了一个曼德博集合查看器,它需要大量复杂的数字来完成结果。在第一种情况下,他们运行了使用Real和Imaginary字段表示为类的代码。然后,他们将单词“class”更改为“struct”,并重新编译。性能差异非常大。
在这种情况下,不需要分配堆对象也不需要垃圾回收是造成差异的原因。根据您拥有的对象数量,差异可能更加显著或不太明显。在开始调整之前,请测量您的性能,然后再决定是否使用值类型。

2

你在编程中将会大部分时间使用类。往往在后期,值类型的语义会给你带来更多麻烦。

然而,如果你选择使用结构体,请确保它是不可变的。


3
你是否只有5%的时间使用结构体? :p - Pedery

1
在C#中,类和结构体是完全不同的东西。两者的语法几乎相同,但行为完全不同(引用 vs. 值)。结构体的单个实例将使用比等效类更少的内存,但一旦开始构建集合或传递它们,值类型语义将在性能方面(以及可能的内存消耗,具体取决于您正在做什么)杀死您,除非结构体中的数据非常小。我不知道硬数字是多少,但我们谈论的是大约8字节或更少的数量级。
这也可能会影响可维护性和可读性,因为两者看起来非常相似,但行为却如此不同。

推荐的大小应小于16字节。 - ChaosPandion
1
缺乏对值类型的优化器支持是性能杀手。现在这个问题已经解决了(至少在x86架构上),你必须达到大约128字节才会出现性能问题。 - Ben Voigt
1
以下是关于结构体优化改进的一些评论:http://msdn.microsoft.com/zh-cn/magazine/dd569747.aspx - Ben Voigt

1

你可以遵循的一个经验法则是问自己你正在尝试做的事情是否像System.Date如果不是,那么你应该非常质疑使用结构体的决定。


0
结构体是值类型,而类是引用类型。这可能是原因之一。

0

对于简单类型,如果你需要很多个实例,不需要继承,并且是不可变的好选择,那么结构体是一个不错的选择。例如:Point、Rectangle、Decimal和DateTime。

如果你不需要很多实例,那么开销就不重要了,这不应该影响你的决定。如果你将来需要从它派生(或使它成为派生类型),那么它必须是一个类。如果该项不能是不可变的,则结构体不是一个好的选择,因为更改一个实例不会更改任何副本。


0

我相信最常引用的收支平衡点是在结构体中约为12个字节的数据。例如,3个整数。然而,值类型和引用类型之间的区别更加基本,对于成员较少的类型,您应该根据此进行选择。

如果某些东西很大,那就使用类吧 :)


优化器在几个服务包之前得到了修复,现在结构体非常快,例如3或4个双精度浮点数(32字节)。新的平衡点略高,我猜大约是128字节。 - Ben Voigt

0

结构体是值类型,而类是引用类型。因此,如果你要传递引用,从性能上来说,类可以更好,因为你只需要复制一个地址而不是整个结构。然而,如果你要实例化和引用这些对象很多次,结构体将更具优势,因为它们分配在堆栈上。因此,在需要高性能的大量对象或需要值类型语义的小对象中,结构体可能更好。结构体也没有默认构造函数。它们继承自Object,但除此之外它们没有继承。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接