何时对不可变类型使用值和引用类型?(.NET)
本文关键字:引用类型 NET 不可变 类型 何时 | 更新日期: 2023-09-27 17:56:04
对于可变类型,值和引用类型之间的行为差异很明显:
// Mutable value type
PointMutStruct pms1 = new PointMutStruct(1, 2);
PointMutStruct pms2 = pms1;
// pms1 == (1, 2); pms2 == (1, 2);
pms2.X = 3;
MutateState(pms1); // Changes the X property to 4.
// pms1 == (1, 2); pms2 == (3, 2);
// Mutable reference type
PointMutClass pmc1 = new PointMutClass(1, 2);
PointMutClass pmc2 = pmc1;
// pmc1 == (1, 2); pmc2 == (1, 2);
pmc2.X = 3;
MutateState(pmc1); // Changes the X property to 4.
// pmc1 == (4, 2); pmc2 == (4, 2);
但是,对于不可变类型,这种差异不太明显:
// Immutable value type
PointImmStruct pis1 = new PointImmStruct(1, 2);
PointImmStruct pis2 = pis1;
// pis1 == (1, 2); pis2 == (1, 2);
pis2 = new PointImmStruct(3, pis2.Y);
// Can't mutate pis1
// pis1 == (1, 2); pis2 == (3, 2);
// Immutable reference type
PointImmClass pic1 = new PointImmClass(1, 2);
PointImmClass pic2 = pic1;
// pic1 == (1, 2); pic2 == (1, 2);
pic2 = new PointImmClass(3, pic2.Y);
// Can't mutate pic1 either
// pic1 == (1, 2); pic2 == (3, 2);
不可变的引用类型也经常使用值语义(例如,规范示例System.String
):
string s1 = GenerateTestString(); // Generate identical non-interned strings
string s2 = GenerateTestString(); // by dynamically creating them
// object.ReferenceEquals(strA, strB)) == false;
// strA.Equals(strB) == true
// strA == strB
Eric Lippert之前在他的博客(例如这里)上讨论过,值类型经常(当对这个讨论并不重要时)在堆栈上分配的事实是一个实现细节,它通常不应该决定你是否使对象成为值或引用类型。
鉴于不可变类型的行为区别模糊,这为我们决定是不可变类型作为引用类型还是值类型留下了什么标准?
此外,由于不可变地强调值与变量,不可变类型是否应始终实现值语义?
我会说你链接的埃里克的博客文章给了你确切的答案:
我很遗憾文档确实 不关注最相关的东西;由 专注于一个基本上无关紧要的 实施细节,我们放大 该实施的重要性 细节和模糊的重要性 是什么使值类型在语义上 有用。我衷心希望所有这些 解释什么是"堆栈"的文章 而是会花时间解释 "按值复制"到底是什么意思 以及如何误解或误用 "按值复制"可能会导致错误。
如果对象应具有"按值复制"语义,则使其成为值类型。如果它们应该具有"按引用复制"语义,请使其成为引用类型。
他也这么说,我同意:
我总是会做出价值的选择 类型与基于引用的类型 类型是否在语义上 表示值或语义上 引用某事。
有一个重要的不可变类型类别(Eric Lippert也写过一些篇幅),必须实现为崇敬类型:递归类型,如列表节点、树节点等。值类型不能具有循环定义,例如,链表节点具有:
class IntNode
{
private readonly int value;
private readonly IntNode next;
}
NET暗示了String
类的答案。 它是不可变的,但是一种引用类型。 使不可变类型尽可能像值类型。 它是否真的是一种值类型并不重要。
所以我能想到的唯一标准是:如果复制它会很昂贵(String
可能涉及大量复制!),请使其成为引用类型。 如果复制速度很快,请选择值类型。 还要考虑是否需要比较引用 - 这可能是具有不可变引用类型的唯一棘手部分。