字符
首先在C#中,所有的char都是16bit的unicode字符。
另外,介绍了System.Char
类型的一些静态方法和实例方法。
静态方法包括了一个可以从UInt32转成两个Char的方法,名为Char.GetNumericValue()
String
String直接继承于Object,是个引用类型的封闭类,它的对象永远都在堆上,不会出现在线程栈上。
构造字符串
string被C#视为基元类型,不能用new
操作符构造,必须使用简化语法:
1 | class Program |
反编译得到IL代码:
1 | .method private hidebysig static void Main(string[] args) cil managed |
可以看到虽然string是一个object,但是生成新对象的时候用的不是newobj
命令,而是ldstr
(load string)命令。
我们还可以用多个字面值(就是不要用变量的意思)来构造一个字符串,编译器生成IL代码的时候就会把字面值都组合在一起。
逐字字符串
用@
号加在字符串前面,完全不考虑任何转移符,一般用来指定路径或者用在正则表达式中。
字符串是不可变的(immutable)
字符串一经创建,就不可以再更改。
使用EndsWith()
或Substring()
之类的方法返回的字符串如果没有被引用到,会再后面被GC掉。
字符串不可变,意味着在操作或访问字符串的时候不会发生线程同步问题。CLR可以通过一个String对象共享多个完全一致的String内容,减少系统中的字符串数量,从而减少内存,这个叫做字符串驻留(string interning)。
字符串的比较
字符串的比较建议使用String类的静态方法
1 | static Int32 Compare(String strA, String strB, StringComparison comparisonType) |
Compare
方法有多个重载方法,可以自己查一下api文档,有一些选项可以定制比较的规矩。
注意,执行序号(ordinal)相等性检查时,CLR会先检测两个字符串是否包含相同数量的字符,如果不一样直接就返回false了。但是执行对语言文化敏感比较时,CLR会检查所有字符。
字符串驻留
由于字符串是不可变的,所以内存中只会保留字符串的一个实例,引用字符串的所有变量都会指向单独的同一个的字符串对象。
CLR初始化时会创建一个内部哈希表,一开始是空的。key是字符串,value是对托管堆中的String对象的引用。
String类提供了两个静态方法,可以让你访问这个内部哈希表。
1 | public static String Intern(String str); |
这个方法根据传进来的参数计算其哈希码,并检查在内部哈希表中是否存在该字符串,有则直接返回引用,没有的话先创建字符串的副本,然后添加到内部哈希表中,再返回对该副本的引用。如果原始的,作为参数传进来那个字符串再也没人引用,有可能就会被GC回收掉,但是被添加到内部哈希表中的字符串,GC再也管不着了,永远被留下来(为什么?没有什么特别的机制,因为这些字符串被内部哈希表引用着呢)。
1 | public static String IsInterned(String str); |
和上面一个方法类似,只是如果发现哈希表里没有这个字符串,会直接返回null,不会将字符串添加到哈希表中。
注意,虽然上面说到了内存中只会保留字符串的一个实例,但是不意味着每个版本的CLR都是这样的。