为什么值对象不可变且按值复制

本文关键字:复制 不可变 对象 为什么 | 更新日期: 2023-09-27 18:16:56

根据维基百科关于值对象的文章,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关键字。值类型和引用类型的区别如下:

  • 类型为值类型的变量/字段在中分配,并在中声明。如果它们是局部方法变量,则可以是当前堆栈帧。但是,如果它们是已经在堆上的对象的一部分,也可以是堆。
  • 类型为引用类型的变量/字段包含对在堆上分配的对象的引用。

由于值类型是在将变量赋值给另一个变量时"就地"分配的,因此实际上是在复制对象的成员。当您将引用类型变量赋值给另一个变量时,您将引用复制到堆上的相同对象。无论哪种方式,您总是在复制变量的内容。