2019.04.18 读书笔记 深入string
整个.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的更多相关文章
- 2019.04.19 读书笔记 比较File.OpenRead()和File.ReadAllBytes()的差异
最近涉及到流的获取与转化,终于要还流的债了. 百度了一下,看到这样的两条回复,于是好奇心,决定看看两种写法的源码差异. 先来看看OpenRead() public static FileStream ...
- 2019.04.17 读书笔记 checked与unchecked
在普通的编程中,我们是很容易去分析数据的大小,然后给出合理的类型,但是在很多数据库的累计中,缺存在很多隐患,特别是研发时,数据量小,求和也不会溢出,当程序运行几年后,再来一次大求和,隐形的BUG就出来 ...
- 《Linux内核设计与实现》Chapter 18 读书笔记
<Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...
- 2019.03.19 读书笔记 string与stringbuilder的性能
1 string与stringbuilder 并不是stringbuilder任何时候都在性能上占优势,在少量(大约个位数)的字符串时,并不比普通string操作快. string慢的原因不是stri ...
- Core Java读书笔记之String
Java里面的String Conceptually, Java Strings are sequences of Unicode characters. Java里面的String都是Unicode ...
- 2019.03.29 读书笔记 关于params与可选参数
void Method1(string str, object a){} void Method2(string str, object a,object b) { } void Method3(st ...
- 2019.03.29 读书笔记 关于override与new
差异:override:覆盖父类分方法,new 隐藏父类方法. 共同:都不能改变父类自身方法. public class Test { public string Name { get; set; } ...
- 2019.03.28 读书笔记 关于lock
多线程就离不开lock,lock的本质是一个语法糖,采用了监视器Monitor. lock的参数,错误方式有很多种,只需要记住一种:private static readonly object loc ...
- 2019.03.28 读书笔记 关于try catch
try catch 在不异常的时候不损耗性能,耗损性能的是throw ex,所以在非异常是,不要滥用throw,特别是很多代码习惯:if(age<0) throw new Exception(& ...
随机推荐
- hadoop理解
Hadoop的主核心有2部分: 1,HDFS 2, MapReduce 首先: HDFS HDFS(Hadoop Distributed File System,Hadoop分布式文件系统),它是一个 ...
- Linux软件包的管理
RPM软件包 RPM软件包一般的安装位置(分散): 管理程序:/sbin/* /usr/sbin/* 普通用户程序:/bin/* /usr/bin/* rpm -q ===> rpm -quer ...
- 引用数据数据类型Scanner、Random
键盘录入Scanner 获取键盘录入的数据,对获取数据的具体操作进行了封装,只需要调用方法,即可得到键盘录入的数据 A:导包 import java.util.Scanner; ...
- [LeetCode 题解]: Permutation Sequcence
The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- Ocelot
Ocelot——初识基于.Net Core的API网关 Ocelot API网关的实现剖析 微服务网关Ocelot API网关Ocelot 使用Polly 处理部分失败问题 谈谈微服务中的 API 网 ...
- controller 状态码
工具使用lombok表示.如没有使用lombok 请 删除@Getter @Setter 并给get set方法 调用方法: 类型使用 Status ---> public ...
- 小程序:web-view采坑指南
最近负责开发的[广州医保查询]小程序已经发布上线,其中使用web-view组件完成的[在线绑定社保卡]核心流程,遇到了一些坑,现总结如下: 首先,让我们一起看看什么是web-view ? 小程序api ...
- 【Selenium专题】WebDriver启动firefox浏览器
firefox浏览器不需要下载驱动,原生支持,以下是代码运行环境,firefox启动封装在方法startFirefox()中 import org.openqa.selenium.WebDriver; ...
- 对比Vector、ArrayList、LinkedList区别
Vector是Java早期提供的线程安全的动态数组.因为同步是又额外开销的,所以如果不需要线程安全,不建议选择.Vector内部用对象数组保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的 ...
- IE6 IE7 IE8 FF兼容符号
2.区别IE8.IE9 一般来说,我们写的结构比较好的时候,IE8/9下是没区别的.所以可能很少人关注只有IE8或只有IE9才识别的css hack. 因为IE8及以下版本是不支持CSS3的,但是我们 ...