鉴于,
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”在修改可变对象时,该对象的内部指针将指向堆内的另一个内存位置。如果这是正确的,那么这是否意味着堆内的所有对象(引用类型)都是不可变的?
感谢您的关注。
答案 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
指向的对象。
- someClassInstance.SomeName会将其指针移动到堆
中的另一个字符串- someClassInstance.RealtedNames将把它的指针移动到堆内的另一个List&lt;&gt;(101)。
醇>
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
引用的对象的内容。这是行动中可变性的一个主要例子。如果此场景中的所有内容都是不可变的,则SomeName
的设置需要生成someClassInstance
引用的对象的副本,并为其赋予新值。这不会发生在这里,可以通过以下
var obj = someClassInstance; // Both reference the same object
someClassInstance.SomeName = "hello";
Console.WriteLine(someClassInstance.SomeName): // Prints "hello"
答案 2 :(得分:0)
是的,因为它们使用new或malloc放在堆上并且是指针。因此,您只能添加或删除指针引用。从技术上讲,对象本身并不是不可变的,因为它们不是在堆上开始,而是堆上的指针分配是不可变的。