CLR via C# 学习笔记(2012/3/11)
2012-03-11 21:20
218 查看
第14章 字符、字符串和文本处理
字符Char
一个16位Unicode码,包括很多静态方法,例如:IsDigit,IsLetter等,这里就不细说了。
字符串String
这才是需要讨论的重点。string是一个使用率很高的类型,尤其在Web应用中。
String类型直接派生自Object,所以它是一个引用类型,因此,String存在于堆上,而不是线程栈。C#将String视为一个基元类型,也就是说可以直接用文本量表示,而不用使用new运算符来分配。要注意的一点,编译器会将文本常量放在元数据中。多个文本常量通过+连接,将视为一个String而保存到元数据中。
String还有一个特点:不可变。一经创建,String的内容就不能更改,任何更改操作只是创建一个新的String而已。也由于不可变的特点,可以使用字符串留用技术来节省内存。
如果应用程序经常对字符串运行区分大小写、序列式的比较,或者事先知道许多字符串对象都有相同的值,就可利用CLR的字符串留用机制来显著提高性能。下面是一个例子:
第一组比较为True的原因是因为编译器对文本常量是保存在元数据中的,并且只有一份,其他对这个文本常量的引用都是引用元数据中的这个引用。
第二组的值是一样的,但是这是两个不同的字符串,所以引用也就不一样了。
第三组使用了字符串留用的技术,将字符串保存在一个哈希表中,Intern(string s)检查是否有相匹配的,如果存在就返回一个引用,不存在则添加并返回引用。
在前一段时间,现在所在的公司的技术总监当时问了我们一个问题:两个String对象内容一样的,在最后生成时,是一个对象还是两个对象。当时我的回答是两个对象,而总监说只有一个,CLR会帮我们处理好。直至我看到本章后,我就知道总监这个回答不全对,他只说了文本常量只会有一份,正如第一组一样,而第二组就明显也是一样的内容,但是它们是两个对象。
字符串池
字符串池也就是刚刚说的,每个文本常量都会嵌入到元数据中,引用该字符串的代码都会被修改,改为指向在元数据中的地址,这就能够有效地提高性能并减少模块大小。
StringBuilder
StringBuilder是为了解决String的不可变而引入的一个可变字符串。对于在需要经常改变字符串的场合,使用StringBuilder能够有效提高性能,这是因为StringBuilder能够动态增减,当需要的时候调用ToString()方法能够返回所需要的String,能够避免不停地创建String,而导致提前垃圾回收。
使用StringBuilder的时候要注意,如果能够预先知道大概需要多大空间,可以在创建的时候指定空间,因为StringBuilder默认只有16个字符长度,当超出长度时,StringBuilder会自动扩大一倍,但这样会伴随一次的内存分配和数据复制的过程,假如所需空间很大,就会多次重复,这也会影响性能。
本章后面的内容本人没怎么接触过,看了也没什么感受,因此也就不写了。
第15章 枚举
枚举可以想像成是常量的强化版。以前使用一些公用的数值的时候,经常使用常量来定义,但是当我们需要遍历时,常量就处理不了了。现在我们有枚举这一利器,大大方便了我们编程。
枚举直接派生于System.Enum=>System.ValueType=>System.Object,所以枚举是值类型。
由于c#编译器将枚举类型视为基元类型,所以可以使用一些操作符(==,!=,+,-,&,|)等来操作枚举类型的实例。
枚举还有一用处:能够遍历枚举中定义的值:
最后,枚举可以用作标志位:
标志位与一般的枚举区别在于,标志位添加了FlagsAttribute特性。
向枚举添加方法用到了扩展方法功能,感觉上就像是使用枚举方法一样。
字符Char
一个16位Unicode码,包括很多静态方法,例如:IsDigit,IsLetter等,这里就不细说了。
字符串String
这才是需要讨论的重点。string是一个使用率很高的类型,尤其在Web应用中。
String类型直接派生自Object,所以它是一个引用类型,因此,String存在于堆上,而不是线程栈。C#将String视为一个基元类型,也就是说可以直接用文本量表示,而不用使用new运算符来分配。要注意的一点,编译器会将文本常量放在元数据中。多个文本常量通过+连接,将视为一个String而保存到元数据中。
String还有一个特点:不可变。一经创建,String的内容就不能更改,任何更改操作只是创建一个新的String而已。也由于不可变的特点,可以使用字符串留用技术来节省内存。
如果应用程序经常对字符串运行区分大小写、序列式的比较,或者事先知道许多字符串对象都有相同的值,就可利用CLR的字符串留用机制来显著提高性能。下面是一个例子:
public static void Main(string[] args) { string s1 = "Hello"; string s2 = "Hello"; Console.WriteLine(Object.ReferenceEquals(s1, s2)); string s3 = string.Format("{0}", "Hello"); string s4 = "Hello"; Console.WriteLine(Object.ReferenceEquals(s3, s4)); string s5 = string.Intern(s3); string s6 = string.Intern(s4); Console.WriteLine(Object.ReferenceEquals(s5, s6)); }执行结果为:True False True
第一组比较为True的原因是因为编译器对文本常量是保存在元数据中的,并且只有一份,其他对这个文本常量的引用都是引用元数据中的这个引用。
第二组的值是一样的,但是这是两个不同的字符串,所以引用也就不一样了。
第三组使用了字符串留用的技术,将字符串保存在一个哈希表中,Intern(string s)检查是否有相匹配的,如果存在就返回一个引用,不存在则添加并返回引用。
在前一段时间,现在所在的公司的技术总监当时问了我们一个问题:两个String对象内容一样的,在最后生成时,是一个对象还是两个对象。当时我的回答是两个对象,而总监说只有一个,CLR会帮我们处理好。直至我看到本章后,我就知道总监这个回答不全对,他只说了文本常量只会有一份,正如第一组一样,而第二组就明显也是一样的内容,但是它们是两个对象。
字符串池
字符串池也就是刚刚说的,每个文本常量都会嵌入到元数据中,引用该字符串的代码都会被修改,改为指向在元数据中的地址,这就能够有效地提高性能并减少模块大小。
StringBuilder
StringBuilder是为了解决String的不可变而引入的一个可变字符串。对于在需要经常改变字符串的场合,使用StringBuilder能够有效提高性能,这是因为StringBuilder能够动态增减,当需要的时候调用ToString()方法能够返回所需要的String,能够避免不停地创建String,而导致提前垃圾回收。
使用StringBuilder的时候要注意,如果能够预先知道大概需要多大空间,可以在创建的时候指定空间,因为StringBuilder默认只有16个字符长度,当超出长度时,StringBuilder会自动扩大一倍,但这样会伴随一次的内存分配和数据复制的过程,假如所需空间很大,就会多次重复,这也会影响性能。
本章后面的内容本人没怎么接触过,看了也没什么感受,因此也就不写了。
第15章 枚举
枚举可以想像成是常量的强化版。以前使用一些公用的数值的时候,经常使用常量来定义,但是当我们需要遍历时,常量就处理不了了。现在我们有枚举这一利器,大大方便了我们编程。
枚举直接派生于System.Enum=>System.ValueType=>System.Object,所以枚举是值类型。
internal enum Color { White, Red, Green, Blue, Orange }重要提示:枚举类型定义的符号是常量值,所以当编译器遇到一个枚举符号时,就会编译时用数值替换符号,也就是说,在运行时可能并不需要加载枚举所在的程序集。
由于c#编译器将枚举类型视为基元类型,所以可以使用一些操作符(==,!=,+,-,&,|)等来操作枚举类型的实例。
枚举还有一用处:能够遍历枚举中定义的值:
Color[] colors = (Color[])Enum.GetValues(typeof(Color)); Console.WriteLine("Number of symbols defined: " + colors.Length); Console.WriteLine("Value\tSymbol\n------\t-------"); foreach (Color c in colors) { Console.WriteLine("{0,5:D}\t{0:G}", c); }
最后,枚举可以用作标志位:
[Flags] internal enum Actions { None = 0, Read = 0x0001, Write = 0x0002, ReadWrite = Actions.Read | Actions.Write, Delete = 0x0004, Query = 0x0008, Sync = 0x0010 }
Actions actions = Actions.Read | Actions.Delete; //0x005 Console.WriteLine(actions.ToString()); //"Read, Delete"
标志位与一般的枚举区别在于,标志位添加了FlagsAttribute特性。
向枚举添加方法用到了扩展方法功能,感觉上就像是使用枚举方法一样。
相关文章推荐
- CLR Via C# 学习笔记(6) 方法参数相关(out ref params)
- CLR Via C# 学习笔记(1) 基元类型 值类型 引用类型
- CLR Via C# 学习笔记(4) 方法 构造函数
- CLR_VIA_C# 学习笔记(1)
- CLR Via C# 学习笔记(5) 静态构造函数的性能
- CLR via C#学习笔记----知识总概括
- CLR via C# 学习笔记----Nullable Value Types可以指定为Null的值类型
- 『框架设计(第2版)CLR Via C#』学习笔记(002)——将托管代码合并到程序集
- CLR via C# 学习笔记(2012/3/12)
- CLR Via C# 学习笔记(6) 方法参数相关(out ref params)
- CLR via C#学习笔记:C#操作符重载学习( 基于.NET3.5 )
- CLR via C# 学习笔记(2012/3/10)
- CLR Via C# 学习笔记(2) 装箱和拆箱
- CLR Via C# 学习笔记(6) 方法参数相关(out ref params)
- 『框架设计(第2版)CLR Via C#』学习笔记——使用is和as操作符来进行强制类型转换
- CLR Via C# 学习笔记(2) 装箱和拆箱
- CLR Via C# 学习笔记(4) 方法 构造函数
- CLR Via C# 学习笔记(4) 方法 构造函数
- CLR via C# 学习笔记(2012/3/4)
- CLR via C# 学习笔记(2012/3/13)