首先看一段程序: 这个没有什么特殊的地方,相信大家都知道运行结果: 第二个WriteLine使用==比较两个字符串,返回False是因为他们不一致。而最后一个WriteLine返回False,因为a、b的引用不一致。 这个的输出,相信也不会出乎大家的意料。前者返回True,因为==两边的内容相等;后者为False,因为+运算符执行完毕后,会创建一个新的string实例,这个实例与b的引用不一致。 接下来,我们就来说一下string不寻常的地方看一下下面这段代码: 运行一下,结果为: 再一次,没什么意外,==返回true因为他们内容相同,ReferenceEquals返回False因为他们是不同的引用。 运行,结果为: 等一下,这里的hellowWorld与helloWorld2引用一致?这个结果,相信很多人都有些接受不了。这里的helloWorld2与上面的hello + " world"应该是一样的,但为什么ReferenceEquals返回的是True? String.Intern有经验的程序员们,应该知道,一个大型项目中,字符串的数量是巨大的。有些时候会出现几百、几千、甚至几万的重复字符串存在。这些字符串的内容相同,但却会重复分配内存,占用巨额的存储空间,这个肯定是要优化处理的。而C#在处理这个问题的时候,采用的就是普遍的做法,建立内部的池,池中每一个不同的字符串存在唯一一个个体在池中(这个方案在各种大型项目中都能见得到)。而C#毕竟是一种语言,而不是一个面向某个具体领域的技术,所以,它不能将这种内部的池技术,做成全部自动化的。因为我们不知道,将来C#会被使用到何种规模的项目中。如果完全自动化维护这个内部池,可能会在大型项目中,造成内存的巨大浪费,毕竟不是所有的字符串都有必要加到这个常驻的池中的。于是,C#提供了String.Intern和String.IsInterned接口,交给程序员自己维护内部的池。 这段代码将返回True,尽管helloWorld与helloWorld2的引用不同,但他们的内容相同。 第一个WriteLine返回False很好理解,因为String.Copy创建了一个a的新的实例,所以,o与a的引用不用。 这个看起来,与上面的做了同样的事,但为什么WriteLine返回的是False? 首先,需要说明一下ToString的工作方式,它总是返回它自身的引用。o是一个指向“abc”的变量,调用ToString返回的就是这个引用。所以,对于上面的内容,可以这样解释:
String.IsInternedIsInterned,正如它的名字,判断一个字符串是不是已经在内部池中。如果传入的字符串已经在池中,则返回这个字符串对象的引用,如果不再池中,返回null。 第一个WriteLine打印的是“not interned”,因为“xyz”还没有存在于内部池中;第二个WriteLine打印了“xyz”因为现在内部池中有了“xyz”;第三个WriteLine打印True,因为对象引用的就是内部池中的“xyz”。 常量字符串自动被加入内部池改变最后一行代码为: 你会发现,奇怪的事情发生了,这些代码不再输出“not interned”了,并且最后的两个WriteLine输出的是False!发生了什么? 编译器比你想象的要聪明改变最后一行代码为: 运行一下,你会发现运行结果和直接使用“xyz”一样。但这里使用了+运算符啊?编译器不应该知道”x“+"y"+"z"最终的结果吧? 这段代码编译之后,使用Ildasm.exe查看,会看到: Screenshot - ILDasm intern-xyz Main method.png 看到了吧,编译器足够聪明,将”x“+"y"+"z"替换为”xyz“。 本文大部分内容来自:http://broadcast./2010/08/understanding-c-stringintern-m.html,翻译、批注:小匠头 作者:小匠头 |
|