CLR Via C# 3rd 阅读摘要 -- Chapter 5 - Primitive, Reference, and Value Types

 Programming Language Primitive Types

1. 原生类型,FCL类型,CLS兼容性;(争论:在编写代码时究竟应该是优先使用keyword还是FCL类型?个人看法,只能是看情况;作者看法,FCL;C#语言规范,keyword)

  作者认为优先使用FCL类型的理由:

  •   开发人员经常会混淆,比如,很多开发者误认为int在64bit OS中是Int64,在32bit OS中是Int32,实际上C#中int=Int32;
  •   不同语言中同名的原生类型却表达不同的含义,比如,C#中long=Int64,而C++中long=Int32;
  •   FCL中有些方法中会有类型名称作为方法名的一部分,比如,BinaryReader.ReadBoolean/ReadInt32/ReadSingle,这样使用keyword就会不自然;
  •   在开发提供给其他客户使用的库中,要考虑哪些使用非C#的开发人员。 

2.  checked与unchecked,都是溢出给逼的。编译器开关/checked+;

  作者建议: 如果可能,用有符号类型代替无符号类型(无符号类型是CLS所不兼容的);

  •   显式的在那些不希望出现溢出的代码块上使用checked;
  •   如果你认为溢出无所谓,那么也显式的使用unchecked;
  •   如果不显式的使用checked或者unchecked,那么就应该假定溢出就要抛出异常(DEBUG:/checked+,RELEASE:/checked-)。 

3.  System.Decimal是比较特殊的,虽然语言把Decimal当成是原生类型,但是CLR可不这么认为,没有IL指令来维护Decimal值,所以checked或unchecked对Decimal是不起作用的,如果溢出了,那么总是抛出OverflowException。而且要注意的是,Decimal速度比较慢;

4. System.Numerics.BitInteger也比较特殊,它的内部使用了一个UInt32的数组来表示大整数,因此没有溢出的概念,除非内存分配光了,产生一个OutOfMemoryException。 

 Reference Types and Value Types

1. 引用类型:

  •   内存必须分配在堆上;
  •   每个分配在堆上的对象上面的成员都必须被初始化;
  •   对象使用的其他字节都必须设置为0;
  •   托管堆上分配的对象需要GC来回收。

2. struct定义的类型是值类型,值类型将在栈上分配;

3. CLR如何控制类型的字段在内存中的布局: LayoutKind.Auto是默认的(引用类型) ;而CLR对值类型的内存布局采用的是LayoutKind.Sequential。 

 Boxing and Unboxing Value Types

1. 装箱与拆箱的概念太一般了,总之值类型与引用类型之间互相转换就会引发装箱与拆箱行为,而这个过程对性能有负面影响(这点就不如Python了);

2. 对集合类型来说,泛型的容器应该是优先考虑的;

3. 编译器会悄悄的干一些装箱拆箱的勾当,而且干的很隐蔽,所以在使用值类型时要特别注意;

4. 虽然拆箱了的值类型没有类型对象指针,但是你仍然可以调用从类型中继承或覆盖的虚拟方法(比如Equals,GetHashCode,ToString),call(不会装箱)与callvirt(会装箱);

5. 为什么不能通过使用接口来改变装箱类型中的字段,技术上是可行的,但是不要这么干;

6. 对象相等:

  • 理解ReferenceEquals()、静态Equals()、实例Equals()、operator==之间的关系;
  • 相等必须是自反、对称、传递、一致性的; 
  • 不要重写static Object.ReferenceEquals()和static Equals(); 
  • 总是为值类型重写instance Equals()和operator==(),operator!=();
  • 为引用类型重些instance Equals(); 
  • 实现System.IEquatable<T>接口的Equals方法,以及System.IComparable<T>.CompareTo方法,这些是类型安全的。  

 Object Hash Codes

1. 用于Hastable和Dictionary的类型需要生成一个散列值作为key。而这应该尽量避免,实在避免不了,应该遵循几个规则: 

  • 如果两个对象相等,那么必须生成相同的散列值;
  • 任何对象A,A.GetHashCode()在生存期间散列值不变;
  • 散列函数必须根据不同输入生成随机分布的整形值;
  • 散列函数应该用到至少一个不可变的字段;
  • 算法应该尽可能的快;
  • 别存储散列值。  

 The Dynamic Primitive Type

1. dynamic就是System.Object,只是编译器会做隐式的类型转换;

2. dynamic和var是不同的:

  • var只是一个一个语法糖,只能用于局部变量;
  • dynamic可以用于局部变量,字段,参数;
  • 可以把一个表达式转成一个var,而不能转成dynamic;
  • 在声明时,必须初始化一个var变量,而不必初始化dynamic。 

 本章小结

  本章主要讲述的是不同种类的类型(原生类型,引用类型、值类型,动态原生类型),以及这些类型的共性与特性。解释了装箱和拆箱的作用,哪些情况下会发生,如何来避免。还有需要特别注意的在override System.Object的方法(Equals, GetHashCode...)时的重要规则,还有dynamic与var之间的差别。

posted @ 2010-03-18 17:25  bengxia  阅读(250)  评论(0编辑  收藏  举报
无觅相关文章插件,快速提升流量