值相同的不同string实例堆存储机制及代码结果解析
关于C#字符串引用相等性的两个问题解析
嘿,咱们来一步步拆解这两个问题,结合C#字符串的核心特性解释清楚~
首先先看你给出的代码示例:
string A = "A"; string A1 = "A"; string B = "AB".Substring(0,1); Console.WriteLine("{0}", object.ReferenceEquals(A, A1)); //Returns True Console.WriteLine("{0}", object.ReferenceEquals(A, B)); //Returns False
问题1:object.ReferenceEquals(A, A1)返回True,是否可理解为二者指向堆中同一个"A"实例?若理解有误,如何用String不可变性解释该结果?
你的理解完全正确!A和A1确实指向**字符串驻留池(属于堆内存的一部分)**里的同一个"A"实例,而字符串的不可变性正是CLR敢复用这个实例的核心原因:
- 字符串的不可变性意味着:一旦一个字符串实例被创建,它的内容就永远无法被修改。所有看似修改字符串的操作(比如拼接、
Substring),本质上都是返回一个全新的字符串实例,原实例的内容丝毫不受影响。 - 因为字符串不可变,CLR可以放心地把编译时就能确定的字符串字面量(比如你代码里的"A")加入字符串驻留池,避免重复创建内容相同的实例以节省内存。所以当你声明
string A = "A"和string A1 = "A"时,CLR会直接让两个变量指向驻留池里的同一个实例,这就是ReferenceEquals返回True的原因。
问题2:object.ReferenceEquals(A, B)返回False,是否意味着"AB".Substring(0,1)的结果在堆中与"A"分开存储?
没错!这个结果确实说明"AB".Substring(0,1)生成的"A"是堆上的一个独立实例,和A指向的驻留池里的"A"是分开存储的:
Substring是运行时动态生成的字符串,CLR默认不会把这类运行时生成的字符串自动加入驻留池。即使它的内容和驻留池里的字符串完全一致,也会在堆上创建一个新的实例。- 如果你想让这个运行时生成的字符串也加入驻留池,可以手动调用
string.Intern(B)方法,之后再用ReferenceEquals比较A和B就会返回True了。不过这种操作只在处理大量重复字符串的场景下才有性能优化的意义,日常开发没必要随便使用。
内容的提问来源于stack exchange,提问作者LONG
相关产品推荐
相关产品推荐

