堆中的所有对象都是不可变的吗

本文关键字:不可变 对象 | 更新日期: 2023-09-27 18:21:03

给定,

public class SomeClass {
     public string SomeName{get;}
     public List<string> RelatedNames{get;}
}
public class Program{
     public void Main(){
          var someClassInstance = new SomeClass(){ SomeName = "A", RelatedNames = new List<string>(1){ "a" }};
          // So, now someClassInstance have been allocated some memory in heap = 1 string object and a list with 1 string object.
          // Since SomeClass is mutable, it could be modified as below
          someClassInstance.SomeName = "Now This is much more than a name";
          someClassInstance.RelatedNames = someClassInstance.RelatedNames.AddRange(new List<string>(100} { "N","o","w".....});
         //Now what happens inside heap?
         //1.someClassInstance.SomeName will move it's pointer to another string inside heap
         //2.someClassInstance.RealtedNames will move it's pointer to another List<>(101) inside heap.
         //Is it correct? Then where is 'mutability' ?
     }
}

正如上面的评论中提到的,"AFAIK"在修改可变对象时,该对象的内部指针将只指向堆内的另一个内存位置。如果这是正确的,那么这是否意味着堆(引用类型)中的所有对象都是不可变的?

谢谢你的关心。

堆中的所有对象都是不可变的吗

可变性在哪里?就在那里:

someClassInstance.SomeName = "Now This is much more than a name";
someClassInstance.RelatedNames = new List<string>(100} { "N","o","w".....};

您只是对someClassInstance指向的对象进行了突变

此外,你的例子有点做作。String确实是不可变的,但List不是,所以您可以这样做:

someClassInstance.RelatedNames.Add("HELLO!");

然后你就突变了someClassInstance.RelatedNames指向的对象。


编辑:我看到你改变了你的问题。那么:

  1. someClassInstance.SomeName将其指针移动到堆内的另一个字符串
  2. someClassInstance.ReatedNames会将其指针移动到另一个列表<>(101)堆内

1是真的,因为String设计为不可变。这就是为什么有StringBuilder类以备您需要可变字符串时使用。

2是false,因为List不是这样实现的。也许这就是你困惑的根源。尽管如此,当您调用AddRange时,someClassInstance.RelatedNames仍将指向同一个实例,但该实例的内部状态将发生更改(很可能,其支持数组将更改为指向不同的数组对象,其计数现在将为101)。事实上,引用不能根据对其引用的对象调用的操作而神奇地改变

这些都没有改变someClassInstance的内部状态发生突变的事实。

默认情况下,CLR中的对象绝对不是不可变的。这里有一点混乱,因为您在示例中使用了string,它是一种实现为不可变类型的类型。这当然不是.Net中的默认情况,而且可变性比不变性更常见。

以这条线为例

someClassInstance.SomeName = "Now This is much more than a name";

本声明中有三个感兴趣的对象。

  • someClassInstance.SomeName引用的对象
  • 值为"Now this is much more than a name"的字符串
  • "someClassInstance"引用的对象`

这三个值都存在于堆中。此语句的执行将使someClassInstance引用的对象的内容发生变化。这是行动中可变性的一个典型例子。如果这个场景中的所有内容都是不可变的,那么SomeName的设置将需要生成someClassInstance引用的对象的副本,并给它新的值。这在这里不会发生,可以通过以下来证明

var obj = someClassInstance;  // Both reference the same object
someClassInstance.SomeName = "hello";
Console.WriteLine(someClassInstance.SomeName): // Prints "hello"

是的,因为它们是使用new或malloc放在堆中的,并且是指针。因此,您只能添加或删除指针引用。因此,从技术上讲,对象本身并不是不可变的,因为它们一开始不在堆上,但堆上的指针分配是不可变的。