整个.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. iOS应用开发之Persistence持久化[转]

    持久化(Persistence) 持久化(Persistence)意思就是当你退出app的时候它还会存在.NSUserDefaults就是一个非常简单的持久化方案,不过这有限制,它只能是很小的东西,通 ...

  2. 编写高质量代码改善C#程序的157个建议——建议77: 正确停止线程

    建议77: 正确停止线程 开发者总尝试对自己的代码有更多的控制.例如,“让那个还在工作的线程马上停止下来”.然而,并非我们想怎样就可以怎样的,这至少涉及两个问题. 第一个问题 正如线程不能立即启动一样 ...

  3. C#函数式程序设计之泛型(上)

    在面向对象语言中,我们可以编写一个元素为某个专用类型(可能需要为此创建一个ListElement)的List类,或者使用一个非常通用.允许添加任何类型元素的基类(在.NET中,首先想到的是System ...

  4. 设计模式11---组合模式(Composite Pattern)

    一.组合模式定义 将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性.Compose objects into tree structures to re ...

  5. oracle 非sys用户创建新用户 授权后 plsql看不到视图

     问题: oracle 非sys用户创建新用户 授权后  plsql看不到视图 答案: 新用户查询视图时,视图名称前需要添加 视图所属用户. 如user用户新建newUser用户,newUser用户查 ...

  6. Echarts X轴内容过长自动隐藏,鼠标移动上去显示全部名称方法

    最近公司做项目,使用echarts做开发,碰到一些数据的名称很长导致图例展示的效果不是很好,自己写了一个方法,当X轴内容过长时自动隐藏,鼠标移动上去显示全部名称 样例: 图二是鼠标移动到名称显示的,怎 ...

  7. Arduino I2C + 温湿度传感器AM2321

    (2015.5.17:本日志的内容有所更新,参见<使用Arduino Wire Library读取温湿度传感器AM2321>.) AM2321是广州奥松电子生产的数字式温湿度传感器.虽是国 ...

  8. stderr和stdout(printf、fprintf、sprintf)(转)

    本文转自:http://www.cnblogs.com/zhangyabin---acm/p/3203745.html 先区分一下:printf,sprintf,fprintf. 1,printf就是 ...

  9. CLR via C# 读书笔记-27.计算限制的异步操作(上篇)

    前言 学习这件事情是一个习惯,不能停...另外这篇已经看过两个月过去,但觉得有些事情不总结跟没做没啥区别,遂记下此文 1.CLR线程池基础 2.ThreadPool的简单使用练习 3.执行上下文 4. ...

  10. Webbench的使用

    Webbench是一个在linux下使用的非常简单的网站压测工具. 它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力 ...