CLR via C#学习笔记-第十四章-字符、字符串和文本处理
14.1 字符
Char结构
Char结构提供的字段
每个字符都是System.Char结构的实例,Char类型提供了两个公共只读常量字段:MinValue('\0')和MaxValue('\uffff\)。
Char实例能调用的方法
为Char的实例调用静态GetUnicodeCategory方法返回System.Globalization.UnicodeCategory枚举类型的一个值,表明该字符是由Unicode标准定义的什么字符。
Char类型提供了几个了静态方法,大多数都在内部调用了GetUnicodeCategory方法,并简单的返回true、false。
Char可以转成数值
转型(强制类型转换)可以将Char转成数值。
14.2 System.String类型
String类型
String派生自Object,因此String对象(他的字符数组)总是存在于堆上。
构造字符串
构造String对象
C#不允许使用new操作符从字面值字符串构造String对象
//不允许 String s=new String("test"); //允许 String s="test";
构造对象新实例的IL指令使newobj,但构造String对象使用的是Idstr,证明CLR实际是用一种特殊方式构造字面值String对象。
如果使用unsafe代码,可以从一个Char*或Sbyte*构造一个String,这时要使用new操作符,并调用由String类型提供的构造器。
操作字符串
定义包含回车和换行符的字符串的正确方法
String s="Hi"+Environment.NewLine+"there";
可以使用+操作符将几个字符串连接在一起
String s="Hi"+" "+"there";
上述代码中,所有字符串都是字面值,所以C#能在编译时连接他们,最终只将一个字符串放到模块的元数据中。
对非字面值字符串使用+操作符,连接则在运行时进行,运行时连接不要使用+操作符,这样会在堆上创建多个字符串对象,影响性能,建议使用StringBuilder
逐字字符串
通常用于指定文件或目录的路径,或者与正则表达式配合使用。
//指定应用程序路径 String file="C:\\Windows\\System32\\Notepad.exe"; //使用逐字字符串 String file=@"C:\Windows\System32\Notepad.exe";
二者元数据没有任何区别,但后者可读性更好
字符串是不可变的
String对象不可变,字符串一经创建便不能更改、变长变短或者修改其中的任何字符。
它允许在一个字符串上执行各种操作而不实际地更改字符串,字符串不可变还意味着在操纵或访问字符串时不会发生线程同步问题。
字符串留用
CLR可通过一个String对象共享多个完全一致的String内容。
比较字符串
相等性或排序
Equals
Compare
StartsWith
EndsWith
字符串留用
CLR初始化时创建一个内部哈希表
表中key是字符串,值是对托管堆中的String对象的引用。
//使用这两个方法可以访问内部的哈希表 public static String Intern(String str); public static String IsInterned(String str);
Intern方法
第一个方法Intern获取一个String获取它的哈希码,并在内部哈希表中检查是否有相匹配的,如果有完全相同的就返回对现有String的引用,不存在就创建字符串的副本。返回对该副本的引用。
GC不能释放内部哈希表引用的字符串,除非卸载或进程终止。
IsInterned方法
IsInterned方法也获取一个String,在内部哈希表中查找,有匹配的就返回对这个歌留用字符串对象的引用,没有就返回null,不添加到哈希表。
使用字符串留用提升性能
除非显式调用Intern方法,否则永远不要以字符串已留用为前提写代码
使用字符串留用可以用来提升性能
private static int NumTimesWordAppearsIntern(String word,String[] wordlist){ //假定wordlist中所有数组元素都引用已留用的字符串 word=String.Intern(word); int count=0; for(int wordnum=0;wordnum<wordlist.Length;wordnum++){ if(Object.ReferenceEquals(word,wordlist[wordnum])) count++; } return count; }
wordlist会包含对堆中同一个String对象的多个引用,比较指针就能知道单词是否在数组里。
字符串池
编译源代码时,编译器必须处理每个字面值字符串,并在托管模块的元数据中嵌入。
同一个字符串在源代码中多次出现把他们嵌入元数据会使生成的文件无谓地增大。
编译器只在模块的元数据中只把字面值字符串写入一次。引用该字符串的所有代码都被修改成引用元数据的同一个字符串。
编译器将单个字符串的多个实例合并成一个实例显著减少模块的大小。
14.3 高效率构造字符串
StringBuilder类型
FCL提供了System.Text.StringBuilder类型对字符串和字符进行高效动态处理,并返回处理好的String对象。
可以将StringBuilder想象为创建String对象地特殊构造器,方法获取地应该是String而不是StringBuilder。
StringBuilder的成员特性
StringBuilder对象包含一个字段,该字段引用了Char结构构成的数组。
利用StringBuilder的各个成员来操纵该字符数组,高效率缩短字符串或更改字符串中的字符。
字符串变大超过了实现分配的字符数组大小,StringBuilder会自动分配新的、更大的数组,复制字符并开始使用新数组,前一个数组被GC。
StringBuilder转换成String
用StringBuilder对象构造好字符串后,调用StringBuilder的ToString即可将StringBuilder字符数组转换成String,这样会在堆上新建String对象,其中包含调用ToString时存在于StringBuilder的字符串,之后可以继续处理StringBuilder中的字符串,以后可以再次调用ToString把它转换成另一个String对象。
构造StringBuilder对象
和String不同,StringBuilder对CLR来说没什么特别。
StringBuilder不是基元类型,要这样构造StringBuilder对象:
StringBuilder sb=new StringBuilder();
StringBuilder构造器参数
StringBuilder提供了许多构造器,其中参数有:
①最大容量,指定了能放到字符串中的最大字符数默认是Int32.MaxValue,约20亿。一般不用更改,构造好了后就不能再变了。
②容量,Int32值,指定了StringBuilder维护的字符数组的长度1,默认16.追加字符时StringBuilder会检测数组会不会超过设定的容量,超过的话会自动倍增容量。
③字符数组,由Char结构构成的数组,负责维护字符串的字符内容。
StringBuilde在堆上分配新对象的情况
StringBuilder大多数成员都能更改字符数组的内容,不会造成在托管堆上分配新对象。
StringBuilder只有以下两种情况才会分配新对象动态构造字符串,长度超过了设置的容量
①调用StringBuilder的ToString方法
②StringBuilder和String提供的方法不完全对应。需要经常相互转换来使用需要的方法
获取对象的字符串表示:ToString
可调用ToString方法获取任何对象的字符串表示。
Object定义了一个public、virtual、无参的ToString方法,所以任何类型的实例上都能调用该方法。
Object实现的ToString只是返回对象所属类型的全名。任何想要提供合理方式获取对象当前值的字符串表示就应该重写ToString。
解析字符串来获取对象:Parse
Parse解决了如何获取字符串并得到它的对象表示。
能解析字符串的任何类型都提供公共静态方法Parse。
方法获取一个String并返回类型的实例。