今天偶然看到String.Intern这个方法感觉特别好奇,查了一下msdn发现有这么一段描述
公共语言运行时维护名为暂存池,其中包含对每个声明或在程序中以编程方式创建的唯一字符串的单个引用的表,从而节约的字符串存储空间。因此,存在一个实例并用特定值的文字字符串的仅一次在系统中
这里面提到了暂存池,关于这个有很多种叫法,在这里还是使用msdn的翻译:”暂存池“。有时候会经常讨论:”多个相同的字符串到底会不会被保存到暂存池中么“,来做几个测试就明白了
var a = "123";
var b = "123";
var c = "1" + "2" + "3";
var d = String.Empty;
for (int i = 1; i < 4; i++)
{
d += i.ToString();
}
代码中声明了四个变量,变量的值是相同的,那么地址是否相同呢?
在immediate window中查看结果如下:
&a
0x0780ec08
*&a: {37688668}
&b
0x0780ec04
*&b: {37688668}
&c
0x0780ec00
*&c: {37688668}
&d
0x0780ebfc
*&d: {37688840}
以变量a为例 :&a表示要获取变量a的地址,下面的 0x0780ec08表示该变量在栈上的地址,对应的*&a:{37688688}表示 0x0780ec08地址中存储的值,对于引用类型而言该值也是一个地址,所以{37688688}这个地址应该是指向了字符串的存储位置。当然,这个也可以通过Object.ReferenceEquals这个方法来验证两个对象是否相等,对比结果如下:
a和b:True
a和c:True
a和d:False
所以从上述两种测试结果看发现变量a、变量b、变量c的地址是相同的,唯独变量d的地址不同。所以在这里可以先简单得出一个结论:”在进行初始化时以非编程方式声明的相同值的字符串会被保存在字符串暂存池的同一位置“
其实我自己得出的这个结论和文章开头引用的msdn对暂存池的定义存在冲突的地方,接下来通过String.Intern这个神奇的方法来证明一个字符串是否是在暂存池,首先先看Intern方法的定义:
Intern 方法使用暂存池来搜索字符串的值等于 str。如果存在这样的字符串,则返回它在暂存池中的引用。如果该字符串不存在,对引用 str 添加到暂存池中,则返回该引用。
测试代码:
var x = "abc";
var y = new StringBuilder().Append("a").Append("b").Append("c").ToString();
var z = string.Intern("abc");
上面代码中变量x、变量y、变量z的值是相同的,都是abc。变量z是通过String.Intern从暂存池获取字符串“abc”,所以通过这个变量z就可以看出变量x与变量y谁在暂存池
在immediate window测试结果如下:
&x
0x077fec98
*&x: {38737244}
&y
0x077fec94
*&y: {38737440}
&z
0x077fec90
*&z: {38737244}
Object.ReferenceEquals比较结果如下:
x==y:False
x==z:True
结果很明先,变量x是保存在暂存池中的,变量y由于是通过“编程”的方式声明的,所以不再暂存池中。当然可能有人要问,如果上面的代码把变量x声明去掉呢?变量y会不会被保存到暂存池?其实去掉也是一样的,去掉变量x的声明以后当String.Intern(“abc”)暂存池查找变量abc时发现这个值不存在,会将abc这个字符串值添加到暂存池并返回一个新的地址引用给变量z,所以变量z和变量y的引用地址还是不相同。
结束
从上述的测试结果可以看出,当程序在初始化的时候会将以非编程方式声明的字符串变量保存到暂存池,从而节约字符串存储空间,但是在msdn的定义中也提到以编程方式创建的字符串也会保存到暂存池,这个与上述的测试结果不符,或者是我个人理解有错误,如果哪位朋友有更好的理解或测试方法还望不吝赐教 :D
测试使用了vs2015+Framework4.6。_