字符串驻留与内存地址疑问:IsInterned为true但引用不等?
问题解析:字符串驻留与
ReferenceEquals的矛盾 先看你贴出的代码:
var a = "ab"; var b = string.Concat("a", "b"); Console.WriteLine(string.IsInterned(a)!=null); //True Console.WriteLine(string.IsInterned(b)!=null); //True Console.WriteLine(object.ReferenceEquals(a,b)); //False
这里的核心误解是对string.IsInterned方法的作用理解偏差:
string.IsInterned的逻辑是:如果传入的字符串内容在驻留表中存在,返回驻留表中的那个字符串引用;如果不存在,返回null。它并不会把传入的字符串本身加入驻留表(除非手动调用string.Intern)。- 变量
a是编译期生成的字符串,编译器会自动将其加入字符串驻留表,所以a的引用就是驻留表中的"ab"对象。 - 变量
b是通过string.Concat在运行时生成的新字符串对象,这个对象并没有被自动加入驻留表。当你调用string.IsInterned(b)时,因为驻留表中已经有a对应的"ab",所以方法返回的是驻留表中的那个引用(也就是a的引用),因此判断IsInterned(b)!=null会得到true——但这并不代表b本身被驻留了,只是存在相同内容的驻留字符串而已。 - 既然
b是运行时新创建的独立对象,a是驻留表中的对象,二者引用自然不同,所以object.ReferenceEquals(a,b)返回false。
如果要让b和a引用相同,需要手动调用string.Intern将b的内容关联到驻留表:
var b = string.Intern(string.Concat("a", "b"));
此时object.ReferenceEquals(a,b)就会返回true,因为string.Intern会直接返回驻留表中已有的"ab"引用(如果存在的话)。
另外补充:编译期的字符串拼接(比如var c = "a"+"b")会被编译器直接优化成"ab",所以c会自动指向驻留表中的对象,和a引用相同,这也是你一开始误以为string.Concat会有同样行为的原因,但string.Concat是运行时执行的方法,编译器无法提前优化。
内容的提问来源于stack exchange,提问作者Adam Kawecki
相关产品推荐
相关产品推荐

