根据维基百科关于值对象的文章,c#值对象既不可变又可以复制属性。
如果它们是不可变的,为什么要复制呢?即使它有助于内存局部性,这种优化是否足以使其成为默认行为?
编辑:哦,我想我误解了不变性。那么,不变性是否意味着你不能单独修改属性,但你可以替换现有对象的整个内部?但这是否违反了"如果两个事物相等,它们将永远相等"?
那么,不变性是否意味着你不能单独修改属性,但你可以替换现有对象的整个内部?
是的。
但这不违反"如果两件事是相等的,它们将永远相等"吗?
。为什么呢?如果您替换现有对象的内部结构,您将得到一个具有不同内部结构的新对象。
我不同意给定的说法,但我会尝试解释我认为他们想说的。
结构类型不可变的事实意味着
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#的实际工作方式是一致的,尽管他们的措辞不是我通常看到的,或者我是怎么说的。请注意,其他文档可能会有不同的声明,乍一看似乎与这些声明完全矛盾。
所有内容都通过value复制,除非您使用ref
关键字。值类型和引用类型的区别如下:
- 类型为值类型的变量/字段在中分配,并在中声明。如果它们是局部方法变量,则可以是当前堆栈帧。但是,如果它们是已经在堆上的对象的一部分,也可以是堆。
- 类型为引用类型的变量/字段包含对在堆上分配的对象的引用。
由于值类型是在将变量赋值给另一个变量时"就地"分配的,因此实际上是在复制对象的成员。当您将引用类型变量赋值给另一个变量时,您将引用复制到堆上的相同对象。无论哪种方式,您总是在复制变量的内容。