深入理解C#中的String(2)

先看一个例子:

string str1 = "string"; string str2 = "string"; string str3 = "stringstring"; string str4 = "string" + "string"; string str5 = str1 + "string"; Console.WriteLine(ReferenceEquals(str1, str2)); Console.WriteLine(str1 == str2); Console.WriteLine(ReferenceEquals(str3, str4)); Console.WriteLine(str3 == str4); Console.WriteLine(ReferenceEquals(str3, str5)); Console.WriteLine(str3 == str5); Console.ReadKey();

不出意外结果都应该为True,True,True,True,True,True,但是结果却是True,True,True,True,False,True,str3和str5不是一个对象,他们不是指向同一个地址,为什么呢?经过查看IL代码发现,str5在IL代码中调用了Concat函数将str1和"string"进行了拼接,那这个Concat函数到底做了什么。

public static string Concat(string str0, string str1) { if (IsNullOrEmpty(str0)) { if (IsNullOrEmpty(str1)) { return Empty; } return str1; } if (IsNullOrEmpty(str1)) { return str0; } int length = str0.Length; string dest = FastAllocateString(length + str1.Length); FillStringChecked(dest, 0, str0); FillStringChecked(dest, length, str1); return dest; }

FastAllocateString函数负责分配长度为str0.Length+str1.Length的空字符串dest,FillStringChecked分别将str0和str1复制到dest中,最后生成由str0和str1连接成的字符串,这样不会再去字符串驻留池中查找是否存在和dest相同的字符串,而是直接生成一个新的对象。所以字符串变量和字符串常量进行拼接后会直接生成一个新的对象,绕过驻留池检查。

而字符串常量拼接不会产生新的字符串,除非驻留池中没有与之拼接后字面值相等的字符串。我们来看看IL代码:

IL_0001: ldstr "string" IL_0006: stloc.0 IL_0007: ldstr "string" IL_000c: stloc.1 IL_000d: ldstr "stringstring" IL_0012: stloc.2 IL_0013: ldstr "stringstring" IL_0018: stloc.3 IL_0019: ldloc.0 IL_001a: ldstr "string" IL_001f: call string [mscorlib]System.String::Concat(string,string) IL_0024: stloc.s str5 IL_0026: ldloc.0 IL_0027: ldloc.1

str3和str4的字面值是相等的,都是"stringstring",str3先于str4被初始化,当str4被初始化的时候,由于其字面值和str3相等,所以CLR会将str3指向的地址赋给str4,所以str3和str4引用是相等的。

至于"=="操作符的得到的结果都是True是因为"=="操作符会调用String.Equal方法,IL代码如下:

IL_0032: call bool [mscorlib]System.String::op_Equality(string,string)

op_Equality最终会调用String.Equal函数,Equal函数的比较步骤是先比较两个对象的引用是否相等,不相等的话再对值进行比较,比较值时是按位比较的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/848fe2050d426691b1fa9f5b6e8739d7.html