Understanding String Interning, 理解字符串驻留
字符串驻留是.Net关于字符串管理的一种规则:
例如:
String a = "HelloWorld";
string b = "Hello"+"World";
这样两种相同的字符串是怎样存储的呢?难道他们是为每个String保存一个引用,然后在堆中分配一块内存吗?
实际上,字符串a和b所存储的位置一样,
String.ReferenceEqual(a, b) // True
但是如果使用
String a = "HelloWorld"
String b = "Hello" + "World";
String d = "Hello";
String c = d + "World";
String.ReferenceEqual(a, c) // false;
为什么值一样地址不一样呢?让我们从String的保存方式说起
String被保存在HashTable中,值做为索引,新创建一个字符串时会先搜索String中是否包括所以String a, b所指向的位置一样
而 string c = d + '"World"; 与 String s = "HelloWorld"不同,看看IL就应该明白了
代码:
1: using System;
2: using System.Collections.Generic;
3:
4: class Program
5: {
6: static void Main()
7: {
8: string a = "HelloWorld";
9: string b = "Hello" + "World";
10: string d = "Hello";
11: string c = d + "World";
12:
13: Console.WriteLine(String.ReferenceEquals(a, b));
14: Console.WriteLine(String.ReferenceEquals(a, c));
15: }
16: }
结果:
1: True
2: False
IL代码:
1: .method private hidebysig static void Main() cil managed
2: {
3: .entrypoint
4: // Code size 63 (0x3f)
5: .maxstack 2
6: .locals init (string V_0,
7: string V_1,
8: string V_2,
9: string V_3)
10: IL_0000: nop
11: IL_0001: ldstr "HelloWorld"
12: IL_0006: stloc.0
13: IL_0007: ldstr "HelloWorld"
14: IL_000c: stloc.1
15: IL_000d: ldstr "Hello"
16: IL_0012: stloc.2
17: IL_0013: ldloc.2
18: IL_0014: ldstr "World"
19: IL_0019: call string [mscorlib]System.String::Concat(string,
20: string)
21: IL_001e: stloc.3
22: IL_001f: ldloc.0
23: IL_0020: ldloc.1
24: IL_0021: call bool [mscorlib]System.Object::ReferenceEquals(object,
25: object)
26: IL_0026: call void [mscorlib]System.Console::WriteLine(bool)
27: IL_002b: nop
28: IL_002c: ldloc.0
29: IL_002d: ldloc.3
31: IL_0033: call bool [mscorlib]System.Object::ReferenceEquals(object,
32: object)
33: IL_0038: call void [mscorlib]System.Console::WriteLine(bool)
34: IL_003d: nop
35: IL_003e: ret
36: } // end of method Program::Main
37:
请注意 10,12行所使用的指令为ldstr(load string), 而string c = b + "World"所使用的指令为Contact, 产生的结果不同,但是实际上 a和c的值一样, 怎样可以让他们指向同样的地址呢?
我们把string.ReferenceEquals(a, c)修改为 string.ReferenceEquals(a, string.Intern(c))再运行下
结果变为
1: True
2: True
原因是String.Intern会先判定是否存在参数的值是否存在与保存HashTable,而后修改地址, 于是c所保存的地址与a一致