.NET源码学习 [算法2-数组与字符串的查找与匹配]( 三 )


.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
据测试后可知 , 即使已存在一个空字符串 , 用以上两种方法继续进行定义变量 , 均不会重新申请新的内存空间 , 而是每次去指向固定的静态只读内存区域 。
据CLR中的相关信息及本人的理解 , 二者仅仅在优化方面稍有差别 , string.Empty是C#对""在语法级别的优化 。也就是说 , ""是通过CLR(Common Language Runtime公共语言运行库)进行优化的 , CLR会维护一个字符串池 , 以防在堆中创建重复的字符串 。而string.Empty是一种C#语法级别的优化 , 是在C#编译器将代码编译为IL(即MSIL)时进行了优化 , 即所有对string类的静态字段Empty的访问都会被指向同一引用 , 以节省内存空间 。
还有两外两个字段 。这两字段从命名来看 , 一个变量存储的是字符串的长度;另一个存储的是第一个字符 。有关二者的应用及更多内容 , 会在之后的某些方法中进一步解释 。
.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图

.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
3. 一个结构(体)
.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图

.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
该结构体派生自类object下的类ValueType , 从父类来看应该是用于存储某些数据类型间的映射关系 。(下方翻译来自Microsoft Bing)
.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
4. 三种运算符重载
.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图

.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图

.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
首先是运算符“==”和“!=” 。对于两个字符串的比较 , 类String内部从新定义了比较规则 , 即重载了判断运算符 , 使用判断运算符就相当于调用类String内部的Equals()方法 。因此 , 对于字符串而言 , 使用“==”和方法Equals()进行比较 , 是完全等价的 。
一般地 , 对于运算符“==”和Equals()方法 , 在比较方式上还是有所差异 。
(1)对于值类型和字符串 , 这两种方式均只比较内容;
(2)对于除字符串以外的引用类型 , 运算符”==”比较的是在栈中的引用(是否指向同一个对象);方法Equals()比较的是在堆中的内容(是否是同一个对象的引用) 。
但这样就有一个问题 , 我们知道C#对于字符串有某些优化 。一般地 , 内容相同的字符串不会开辟新的堆空间 , 而是指向储存在暂存池(堆的一部分 , Java中称为常量池)中的对象 。但以某些方式创建的字符串对象 , 并不会检查暂存池 , 而是直接在堆中开辟新空间 , 导致内容相同的字符串 , 栈和堆的地址均不同 。那么这样是否会导致判断运算符和方法Equals()出现问题呢?有关该部分的详细内容请转到文末的 [# 有关字符串的比较与暂存池]
.NET源码学习 [算法2-数组与字符串的查找与匹配]

文章插图
其次是ReadOnlySpan<T> 。这是一种较为特殊的运算符重载 , 其中关键字implicit用于声明隐式的自定义类型转换运算符 。它可以实现2个不同类型的隐式转换 , 提高代码的可读性 。但是使用隐式转换操作符之后 , 在编译时会跳过异常检查 , 所以在使用隐式转换运算符前 , 应当在一定程度上确保其不会引发异常并且不会丢失信息 , 否则在运行时会出现一些意外的问题 。该隐式转换 , 是将string类型转换成ReadOnlySpan<char>类型 。

经验总结扩展阅读