注释
此内容由 Pearson Education, Inc. 的许可从 框架设计指南:可重用 .NET 库的约定、习惯和模式(第 2 版)重新打印。 该版于2008年出版,此后该书已于 第三版全面修订。 此页上的一些信息可能已过期。
每个框架设计器面临的基本设计决策之一是将类型设计为类(引用类型)还是结构(值类型)。 充分了解引用类型和值类型行为的差异对于做出此选择至关重要。
我们要考虑的引用类型和值类型的第一个区别是:引用类型是在堆上分配的,会进行垃圾回收;而值类型要么在堆栈分配并在堆栈展开时解除分配,要么内联在包含类型中分配并在其包含类型被解除分配时解除分配。 因此,值类型的分配和解除分配通常比引用类型的分配和解除分配开销更低。
第二个区别是:引用类型的数组不是内联分配的,也就是说数组的元素只是一些引用,指向那些位于堆中的引用类型的实例。 值类型数组是内联分配的,这意味着数组元素是值类型的实际实例。 因此,值类型数组的分配和解除分配比引用类型数组的分配和解除分配便宜得多。 此外,在大多数情况下,值类型数组表现出更好的引用位置。
下一个差异与内存使用情况相关。 值类型在被强制转换为引用类型或它们实现的接口之一时,要执行装箱操作。 它们在被强制转换回值类型时,将执行取消装箱操作。 因为箱子是在堆上分配的对象,会进行垃圾回收,所以装箱和取消装箱操作太多会对堆、垃圾回收器,并最终对应用程序的性能产生负面影响。 相比之下,在对引用类型执行强制转换操作时,不会发生装箱操作。 (有关详细信息,请参阅 装箱和拆箱)。
接下来,引用类型分配会复制其引用,而值类型分配会复制整个值。 因此,大型引用类型的分配比大型值类型的分配便宜。
最后,引用类型通过引用传递,而值类型则按值传递。 对引用类型的实例的更改会影响指向该实例的所有引用。 值类型实例在按值传递时被复制。 当值类型的一个实例被改变时,显然不会影响它的任何副本。 由于副本不是由用户显式创建的,但在传递参数或返回值时隐式创建,因此可以更改的值类型可能会令许多用户感到困惑。 因此,值类型应不可变。
一般说来,框架中的大多数类型应该是类。 但是,在某些情况下,值类型的特征使得使用结构更合适。
✔️ 如果类型的实例较小且通常生存期较短或通常嵌入在其他对象中,请考虑定义结构而不是类。
❌ 避免定义结构,除非类型具有以下所有特征:
它以逻辑方式表示单个值,类似于基元类型(
int
等double
)。它的实例大小低于 16 个字节。
它是不可变的。
它不必频繁装箱。
在所有其他情况下,应将类型定义为类。
部分内容 © 2005, 2009 Microsoft 公司。 保留所有权利。
获得皮尔逊教育公司许可后重印自 框架设计准则:可重用 .NET 库的约定、习惯和模式 ,由 Krzysztof Cwalina 和 Brad Abrams 编写,并作为微软 Windows 开发系列中的出版物之一,于 2008 年 10 月 22 日由 Addison-Wesley Professional 出版。