C# 为什么值对象是不可变的并按值复制?
根据,C#value对象是不可变的,并且是按属性复制的 如果它们是不可变的,为什么要复制?即使它有助于内存的局部性,但这种优化是否足够普遍,使其成为默认行为 编辑:哦,我想我误解了不变性 那么,不变性是否意味着不能单独修改属性,但可以替换现有对象的整个内部构件?但这是否违反了“如果两件事是相等的,它们将永远是相等的” 所以不可变是否意味着不能单独修改属性,但是可以从现有对象中替换整个内部构件 对 但这是否违反了“如果两件事是相等的,它们将永远是相等的”C# 为什么值对象是不可变的并按值复制?,c#,value-objects,C#,Value Objects,根据,C#value对象是不可变的,并且是按属性复制的 如果它们是不可变的,为什么要复制?即使它有助于内存的局部性,但这种优化是否足够普遍,使其成为默认行为 编辑:哦,我想我误解了不变性 那么,不变性是否意味着不能单独修改属性,但可以替换现有对象的整个内部构件?但这是否违反了“如果两件事是相等的,它们将永远是相等的” 所以不可变是否意味着不能单独修改属性,但是可以从现有对象中替换整个内部构件 对 但这是否违反了“如果两件事是相等的,它们将永远是相等的” 不,为什么会这样?如果替换现有对象的内部构
不,为什么会这样?如果替换现有对象的内部构件,则会得到一个具有不同内部构件的新对象。我不同意给定的声明,但我将尝试解释我认为他们打算说什么 结构类型是不可变的这一事实意味着
public struct S { int i; }
public S f() { /* omitted */ }
public void g() { f().i = 3; }
是编译时错误:修改f()
的结果没有意义,因为修改会立即丢失
相比之下
public struct S { int i; }
public S f() { /* omitted */ }
public void g() { var s = f(); s.i = 3; }
很好,但s.i=3
可以解释为重写所有s
:它可以解释为等价于(伪代码)s={3}
,其中{3}
构造一个全新的S
值对象
但这是否违反了“如果两件事是相等的,它们将永远是相等的”
根据他们的解释,这仍然是事实。s.i=3后
,s
是一个全新的值。在分配给s.i
之前,s
等于f()
的结果。在分配给s.i
之后,s
本身发生了根本性的变化,它不仅仅是对该对象属性的修改,你得到了一个全新的对象,它永远不等于任何其他对象,除非是偶然的
他们的解释与C#的实际工作方式是一致的,尽管他们的措辞不是我通常所看到的,也不是我所说的。请注意,其他文档可能会提出不同的声明,乍一看似乎与这些声明完全矛盾。所有内容都是按值复制的,除非您使用
ref
关键字。值类型和引用类型之间的区别是:
- 类型为值类型的变量/字段分配到声明它们的位置。如果它们是局部方法变量,则可以是当前堆栈帧。但是,如果它们是堆上已经存在的对象的一部分,那么它也可以是堆
- 类型为引用类型的变量/字段包含对堆上分配的对象的引用
由于当您将一个变量分配给另一个变量时,值类型是“就地”分配的,因此您实际上是在复制对象的成员。将引用类型变量分配给另一个变量时,就是将引用复制到堆上的同一对象。不管怎样,你总是在复制变量的内容。我认为那篇文章的措辞很糟糕。它试图说的与它看起来所说的相反。值类型不必是不变的。建议使用,但不是必需的
System.Drawing.Rectangle
不是一成不变的。@DavidSchwartz那篇文章很糟糕;其中包括通常的“结构保存在堆栈中,类存储在堆中”胡说八道。鉴于值对象应该是线程安全的:如果我们都可以访问同一个值对象,并且可以在我不知道的情况下替换其内部,我肯定不会称之为“新对象”。但它是一个新对象。每次替换内部对象时,都会得到一个具有不同内部对象的新对象。若您有一个不可变对象并对其进行了修改,那个么您将通过修改获得一个新的不可变对象。这就是不可变对象的语义。在不同的地址?@leewangzhong是,也不是。引用在同一地址,但它是对不同“引用对象”的引用,在新地址。因此值类型不是不可变的,因此默认情况下不会是线程安全的?