堆中的所有对象都是不可变的吗
本文关键字:不可变 对象 | 更新日期: 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
指向的对象。
编辑:我看到你改变了你的问题。那么:
- someClassInstance.SomeName将其指针移动到堆内的另一个字符串
- someClassInstance.ReatedNames会将其指针移动到另一个列表<>(101)堆内
1
是真的,因为String
被设计为不可变。这就是为什么有StringBuilder
类以备您需要可变字符串时使用。
2
是false,因为List
不是这样实现的。也许这就是你困惑的根源。尽管如此,当您调用AddRange
时,someClassInstance.RelatedNames
仍将指向同一个实例,但该实例的内部状态将发生更改(很可能,其支持数组将更改为指向不同的数组对象,其计数现在将为101
)。事实上,引用不能根据对其引用的对象调用的操作而神奇地改变
这些都没有改变someClassInstance
的内部状态发生突变的事实。
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放在堆中的,并且是指针。因此,您只能添加或删除指针引用。因此,从技术上讲,对象本身并不是不可变的,因为它们一开始不在堆上,但堆上的指针分配是不可变的。