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

时间:2012-01-21 02:51:39

标签: c# heap immutability

鉴于,

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”在修改可变对象时,该对象的内部指针将指向堆内的另一个内存位置。如果这是正确的,那么这是否意味着堆内的所有对象(引用类型)都是不可变的?

感谢您的关注。

3 个答案:

答案 0 :(得分:4)

哪里可变?就在那里:

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. 中的另一个字符串   
  3. someClassInstance.RealtedNames将把它的指针移动到堆内的另一个List&lt;&gt;(101)。
  4.   

1是正确的,因为String 设计是不可变的。这就是为什么你需要一个可变字符串的StringBuilder类。

2是错误的,因为这不是List的实现方式。也许这就是你的混乱所在。仍然,当您调用AddRange时,someClassInstance.RelatedNames仍将指向同一个实例,但该实例的内部状态将发生更改(最有可能的是,其后备数组将更改为指向不同的数组对象,它的计数现在是101)。实际上,引用不能基于调用它引用的对象的操作而神奇地改变。

这一切都没有改变someClassInstance内部状态发生变异的事实。

答案 1 :(得分:1)

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

以此行为例

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

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

  • 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"

答案 2 :(得分:0)

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