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

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

public class Example
{
public static void Main()
{
String s1 = "String1";
String s2 = "String1";
Console.WriteLine("s1 = s2: {0}", Object.ReferenceEquals(s1, s2));
Console.WriteLine("{0} interned: {1}", s1,
String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes"); String suffix = "A";
String s3 = "String" + suffix;
String s4 = "String" + suffix;
Console.WriteLine("s3 = s4: {0}", Object.ReferenceEquals(s3, s4));
Console.WriteLine("{0} interned: {1}", s3,
String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes");
}
}
// The example displays the following output:
// s1 = s2: True
// String1 interned: Yes
// s3 = s4: False
// StringA interned: No

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

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.

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.

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.

Compare this method to the Intern method.

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.
//谷歌翻译如下:
公共语言运行库自动维护一个名为intern pool的表,该表包含程序中声明的每个唯一文字字符串常量的单个实例,以及通过调用Intern方法以编程方式添加的任何唯一String实例。 实习池保存字符串存储。 如果为多个变量分配文字字符串常量,则每个变量都设置为在实习池中引用相同的常量,而不是引用具有相同值的几个不同的String实例。 此方法在实习池中查找str。 如果str已经被实现,则返回对该实例的引用; 否则,返回null。 将此方法与Intern方法进行比较。 此方法不返回布尔值。 如果您调用该方法是因为您需要一个布尔值来指示特定字符串是否被实现,则可以使用如下代码。

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

//demo
class Sample {
public static void Main() {
String s1 = "MyTest";
String s2 = new StringBuilder().Append("My").Append("Test").ToString();
String s3 = String.Intern(s2);
Console.WriteLine("s1 == '{0}'", s1);
Console.WriteLine("s2 == '{0}'", s2);
Console.WriteLine("s3 == '{0}'", s3);
Console.WriteLine("Is s2 the same reference as s1?: {0}", (Object)s2==(Object)s1);
Console.WriteLine("Is s3 the same reference as s1?: {0}", (Object)s3==(Object)s1);
}
}
/*
This example produces the following results:
s1 == 'MyTest'
s2 == 'MyTest'
s3 == 'MyTest'
Is s2 the same reference as s1?: False
Is s3 the same reference as s1?: True
*/ //
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. 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. 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. 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.
//谷歌翻译:
公共语言运行库通过维护一个名为intern pool的表来保存字符串存储,该表包含对在程序中以编程方式声明或创建的每个唯一文字字符串的单个引用。因此,具有特定值的文字字符串实例仅在系统中存在一次。 例如,如果将相同的文字字符串分配给多个变量,则运行时将从实习池中检索对文字字符串的相同引用,并将其分配给每个变量。 Intern方法使用实习池来搜索等于str值的字符串。如果存在此类字符串,则返回其在实习池中的引用。如果该字符串不存在,则将对str的引用添加到实习池中,然后返回该引用。 在下面的示例中,字符串s1(其值为“MyTest”)已经被实习,因为它是程序中的文字。 System.Text.StringBuilder类生成一个与s1具有相同值的新字符串对象。对该字符串的引用被分配给s2。 Intern方法搜索与s2具有相同值的字符串。因为存在这样的字符串,所以该方法返回分配给s1的相同引用。然后将该引用分配给s3。引用s1和s2比较不等,因为它们指的是不同的对象;引用s1和s3比较相等,因为它们引用相同的字符串。

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

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

//

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

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

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

string s = "小明";
string s1 = "hello" + s;
string s2 = "hello" + s;
unsafe
{
fixed (char* p = s1)
{
Console.WriteLine("字符串地址= 0x{0:x}", (int)p);
}
fixed (char* p = s2)
{
Console.WriteLine("字符串地址= 0x{0:x}", (int)p);
}
}
//字符串地址= 0x9183c
//字符串地址= 0x91864
//地址不一样,是不同的实例

我们再加一行代码:

 string s = "小明";
string s1 = "hello" + s;
string s2 = s1;
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. C/C++语言中指针数组和数组指针比较区别

    指针数组:int *p[3] 定义一个指针数组,其中每个数组元素指向一个int型变量的地址 意思就是指针的数组,数组里面都是指针 例子: int *p[3];//定义了一个指针数组,有3个成员,每个成 ...

  2. Java Project 转 Dynamic Web Project

    使用eclipse 工具, 右键Java Project 项目 Properties - Project Facets  - Convert to faceted form... - 勾选Dynami ...

  3. Alpha冲刺 - (9/10)

    Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 进一步优化代码,结合自己负责的部分修 ...

  4. LightOJ 1098(均值不等式,整除分块玄学优化)

    We all know that any integer number n is divisible by 1 and n. That is why these two numbers are not ...

  5. MySQL如何查看连接数和状态

    查看连接数 命令:show processlist 如果要是root账号,能够看见当前所有用户的连接.如果是普通账号,只能看到自己占用的连接数.   show processlist只能是列出前100 ...

  6. 无线显示技术:WiDi,WLAN Display,Air Play,Miracast的摘抄

    除了标题提到的WIDI,WLAN Display,Air Play之外,还有Miracast与DLNA的概念,他们之间的区别和联系是什么呢? WIDI: WiDi是英特尔主导的无线音视频传输技术,这也 ...

  7. MVC dropdownlist 下拉框

    List<SelectListItem> items = new List<SelectListItem>(); items.Add(new SelectListItem() ...

  8. 【Selenium专题】FAQ_浏览器_ChromeDriver版本导致报错

    报错信息 Started ChromeDriver port=31984 version=23.0.1240.0 log=C:\workspace\test\chromedriver.log [063 ...

  9. 数数(高维DP)

    T1 数数 [问题描述] fadbec 很善于数数,⽐如他会数将 a 个红球,b 个黄球,c 个蓝球,d 个绿球排成⼀列,任意相邻不同⾊的数⽬. 现在 R 君不知道 fadbec 数的对不对,想让你也 ...

  10. NOIP前刷题记录

    因为本蒻实在太蒻了...对于即将到来的NOIP2018ssfd,所以下决心要把自己近期做过的题目(衡量标准为洛谷蓝题难度或以上)整理一下,归归类,简单地写一下思路,就当作自己复习了吧qwq 本随笔持续 ...