整个.net中,最特殊的就是string类型了,它有着引用类型的特性,又有着值类型的操作方式,最特殊的是,它还有字符串池,一个字符串只会有一个实例(等下就推翻!)。

鉴于之前的《==与Equals》中说过了一些,这里就不多说了,重写了==和Equals,使得只比较值,但是在官网的说明中发现了这样一行代码:

  1. public class Example
  2. {
  3. public static void Main()
  4. {
  5. String s1 = "String1";
  6. String s2 = "String1";
  7. Console.WriteLine("s1 = s2: {0}", Object.ReferenceEquals(s1, s2));
  8. Console.WriteLine("{0} interned: {1}", s1,
  9. String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes");
  10.  
  11. String suffix = "A";
  12. String s3 = "String" + suffix;
  13. String s4 = "String" + suffix;
  14. Console.WriteLine("s3 = s4: {0}", Object.ReferenceEquals(s3, s4));
  15. Console.WriteLine("{0} interned: {1}", s3,
  16. String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes");
  17. }
  18. }
  19. // The example displays the following output:
  20. // s1 = s2: True
  21. // String1 interned: Yes
  22. // s3 = s4: False
  23. // StringA interned: No

  只有在编译时的常量字符串,才能ReferenceEquals两个变量返回True,运行时产生的相同字符串,返回的False,从这个代码里,我们又引入了另外一个方法:string.IsInterned,深入源码,又是引入外部函数的套路,无法看到具体的实现,但是在官网中有如下说明:

  1. The common language runtime automatically maintains a table, called the intern pool, which contains a single instance of each unique literal string constant declared in a program, as well as any unique instance of String you add programmatically by calling the Intern method.
  2.  
  3. The intern pool conserves string storage. If you assign a literal string constant to several variables, each variable is set to reference the same constant in the intern pool instead of referencing several different instances of String that have identical values.
  4.  
  5. This method looks up str in the intern pool. If str has already been interned, a reference to that instance is returned; otherwise, null is returned.
  6.  
  7. Compare this method to the Intern method.
  8.  
  9. This method does not return a Boolean value. If you call the method because you want a Boolean value that indicates whether a particular string is interned, you can use code such as the following.
  10. //谷歌翻译如下:
  11. 公共语言运行库自动维护一个名为intern pool的表,该表包含程序中声明的每个唯一文字字符串常量的单个实例,以及通过调用Intern方法以编程方式添加的任何唯一String实例。
  12.  
  13. 实习池保存字符串存储。 如果为多个变量分配文字字符串常量,则每个变量都设置为在实习池中引用相同的常量,而不是引用具有相同值的几个不同的String实例。
  14.  
  15. 此方法在实习池中查找str 如果str已经被实现,则返回对该实例的引用; 否则,返回null
  16.  
  17. 将此方法与Intern方法进行比较。
  18.  
  19. 此方法不返回布尔值。 如果您调用该方法是因为您需要一个布尔值来指示特定字符串是否被实现,则可以使用如下代码。

  这段文字的大体意思就是,字符串池还分了编译时的字符串池和运行时的字符串池,但是string.IsInterned是只索引编译时的字符串池,如果存在则返回实例引用,如果不存在则返回空。那么demo中的s3是运行时字符串池,尽管值相等,但是不是同一个对象,所以的ReferenceEquals就是False,也就是说string只有一个实例是错误的。然后又发现了另一个有意思的方法:string.Interned

  1. //demo
  2. class Sample {
  3. public static void Main() {
  4. String s1 = "MyTest";
  5. String s2 = new StringBuilder().Append("My").Append("Test").ToString();
  6. String s3 = String.Intern(s2);
  7. Console.WriteLine("s1 == '{0}'", s1);
  8. Console.WriteLine("s2 == '{0}'", s2);
  9. Console.WriteLine("s3 == '{0}'", s3);
  10. Console.WriteLine("Is s2 the same reference as s1?: {0}", (Object)s2==(Object)s1);
  11. Console.WriteLine("Is s3 the same reference as s1?: {0}", (Object)s3==(Object)s1);
  12. }
  13. }
  14. /*
  15. This example produces the following results:
  16. s1 == 'MyTest'
  17. s2 == 'MyTest'
  18. s3 == 'MyTest'
  19. Is s2 the same reference as s1?: False
  20. Is s3 the same reference as s1?: True
  21. */
  22.  
  23. //
  24. The common language runtime conserves string storage by maintaining a table, called the intern pool, that contains a single reference to each unique literal string declared or created programmatically in your program. Consequently, an instance of a literal string with a particular value only exists once in the system.
  25.  
  26. For example, if you assign the same literal string to several variables, the runtime retrieves the same reference to the literal string from the intern pool and assigns it to each variable.
  27.  
  28. The Intern method uses the intern pool to search for a string equal to the value of str. If such a string exists, its reference in the intern pool is returned. If the string does not exist, a reference to str is added to the intern pool, then that reference is returned.
  29.  
  30. In the following example, the string s1, which has a value of "MyTest", is already interned because it is a literal in the program. The System.Text.StringBuilder class generates a new string object that has the same value as s1. A reference to that string is assigned to s2. The Intern method searches for a string that has the same value as s2. Because such a string exists, the method returns the same reference that is assigned to s1. That reference is then assigned to s3. References s1 and s2 compare unequal because they refer to different objects; references s1 and s3 compare equal because they refer to the same string.
  31. //谷歌翻译:
  32. 公共语言运行库通过维护一个名为intern pool的表来保存字符串存储,该表包含对在程序中以编程方式声明或创建的每个唯一文字字符串的单个引用。因此,具有特定值的文字字符串实例仅在系统中存在一次。
  33.  
  34. 例如,如果将相同的文字字符串分配给多个变量,则运行时将从实习池中检索对文字字符串的相同引用,并将其分配给每个变量。
  35.  
  36. Intern方法使用实习池来搜索等于str值的字符串。如果存在此类字符串,则返回其在实习池中的引用。如果该字符串不存在,则将对str的引用添加到实习池中,然后返回该引用。
  37.  
  38. 在下面的示例中,字符串s1(其值为“MyTest”)已经被实习,因为它是程序中的文字。 System.Text.StringBuilder类生成一个与s1具有相同值的新字符串对象。对该字符串的引用被分配给s2 Intern方法搜索与s2具有相同值的字符串。因为存在这样的字符串,所以该方法返回分配给s1的相同引用。然后将该引用分配给s3。引用s1s2比较不等,因为它们指的是不同的对象;引用s1s3比较相等,因为它们引用相同的字符串。

  就是如果编译字符串池,存在,则返回实例,如果不存在,就添加到编译字符串池(这个很重要,我们等下就测试),上面的demo是s1存在,所以s3就指向了编译池的字符串了,和s2不相等了,和s1相等了。

那么我们现在测试一下添加的过程:

  1. //
  2.  
  3. string a = "x";
  4. string b = "x" + a;
  5. string c = "x" + a;
  6.  
  7. Console.WriteLine(object.ReferenceEquals(c, b));//false 因为c 和 b都不在编译池
  8. b=string.Intern(b);//添加到编译池并且返回给b
  9. Console.WriteLine(object.ReferenceEquals(c, b));//false 因为b在编译池,c不在
  10. c=string.Intern(c);//在编译池查找到等值的字符串,返回给c
  11. Console.WriteLine(object.ReferenceEquals(c, b));//true b和c都指向了编译池的字符串。

  经过测试验证,终于弄明白了字符串的来龙去脉。

那么还有疑问:b和c虽然不在编译池,是同一个实例吗?

  1. string s = "小明";
  2. string s1 = "hello" + s;
  3. string s2 = "hello" + s;
  4. unsafe
  5. {
  6. fixed (char* p = s1)
  7. {
  8. Console.WriteLine("字符串地址= 0x{0:x}", (int)p);
  9. }
  10. fixed (char* p = s2)
  11. {
  12. Console.WriteLine("字符串地址= 0x{0:x}", (int)p);
  13. }
  14. }
  15. //字符串地址= 0x9183c
  16. //字符串地址= 0x91864
  17. //地址不一样,是不同的实例

我们再加一行代码:

  1. string s = "小明";
  2. string s1 = "hello" + s;
  3. string s2 = s1;
  4. Console.WriteLine(object.ReferenceEquals(s1,s2));//返回 True

结论:字符串池分编译池和运行池,且ReferenceEqualse是最直接的比较引用地址,如果不相等就返回false,但是可以手动Interned到编译池,如果不存在则添加,如果存在,则引用。

在运行中,每次的string.contact都是产生新的实例,就算编译池中已有,也会重新实例。同字符串只一个实例是错误的。

2019.04.18 读书笔记 深入string的更多相关文章

  1. 2019.04.19 读书笔记 比较File.OpenRead()和File.ReadAllBytes()的差异

    最近涉及到流的获取与转化,终于要还流的债了. 百度了一下,看到这样的两条回复,于是好奇心,决定看看两种写法的源码差异. 先来看看OpenRead() public static FileStream ...

  2. 2019.04.17 读书笔记 checked与unchecked

    在普通的编程中,我们是很容易去分析数据的大小,然后给出合理的类型,但是在很多数据库的累计中,缺存在很多隐患,特别是研发时,数据量小,求和也不会溢出,当程序运行几年后,再来一次大求和,隐形的BUG就出来 ...

  3. 《Linux内核设计与实现》Chapter 18 读书笔记

    <Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...

  4. 2019.03.19 读书笔记 string与stringbuilder的性能

    1 string与stringbuilder 并不是stringbuilder任何时候都在性能上占优势,在少量(大约个位数)的字符串时,并不比普通string操作快. string慢的原因不是stri ...

  5. Core Java读书笔记之String

    Java里面的String Conceptually, Java Strings are sequences of Unicode characters. Java里面的String都是Unicode ...

  6. 2019.03.29 读书笔记 关于params与可选参数

    void Method1(string str, object a){} void Method2(string str, object a,object b) { } void Method3(st ...

  7. 2019.03.29 读书笔记 关于override与new

    差异:override:覆盖父类分方法,new 隐藏父类方法. 共同:都不能改变父类自身方法. public class Test { public string Name { get; set; } ...

  8. 2019.03.28 读书笔记 关于lock

    多线程就离不开lock,lock的本质是一个语法糖,采用了监视器Monitor. lock的参数,错误方式有很多种,只需要记住一种:private static readonly object loc ...

  9. 2019.03.28 读书笔记 关于try catch

    try catch 在不异常的时候不损耗性能,耗损性能的是throw ex,所以在非异常是,不要滥用throw,特别是很多代码习惯:if(age<0) throw new Exception(& ...

随机推荐

  1. redis 通配符 批量删除key

    Redis 中 DEL指令支持多个key作为参数进行删除 但不支持通配符,无法通过通配符批量删除key,不过我们可以借助 Linux 的管道和 xargs 指令来完成这个动作. 比如要删除所有以use ...

  2. javascript总结23:javascript 数据类型与变量

    1  基本类型和引用类型 JavaScript中的数据类型分为两类:基本类型和引用类型 基本类型:直接存储值,画图解释 Number.String.Boolean Undefined.Null 引用类 ...

  3. 机器学习及其matlab实现—从基础到实践

    第1周 MATLAB入门基础 第2周 MATLAB进阶与提高 第3周 BP神经网络 第4周 RBF.GRNN和PNN神经网络 第5周 竞争神经网络与SOM神经网络 第6周 支持向量机(Support ...

  4. nginx+tomcat实现动静态分离

    ===============Tomcat 概述: Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache ...

  5. 理解Javascript中的事件绑定与事件委托

    最近在深入实践js中,遇到了一些问题,比如我需要为动态创建的DOM元素绑定事件,那么普通的事件绑定就不行了,于是通过上网查资料了解到事件委托,因此想总结一下js中的事件绑定与事件委托. 事件绑定   ...

  6. U盘安装Centos6.2

    原文地址:http://www.dedecms.com/knowledge/servers/linux-bsd/2012/0819/8452.html. 第一步:制作系统U盘(略). 第二步:设置BI ...

  7. Java NIO学习-预备知识

    java NIO加入了Channels.Buffers.Selector.通过他们可以为java的io添加非阻塞IO. 一.对于经典java IO库 1.除了Buffered开头的类,其他均没有加缓冲 ...

  8. [LeetCode 题解]: Permutations

    Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...

  9. android之实现底部TabHost

    先说布局文件,如下:利用android:layout_alignParentBottom="true" 实现底部显示 <?xml version="1.0" ...

  10. leetcode 存在重复元素

    给定一个整数数组,判断是否存在重复元素. 如果任何值在数组中出现至少两次,函数返回 true.如果数组中每个元素都不相同,则返回 false. 示例 1: 输入: [1,2,3,1] 输出: true ...