Learning CLR via C#(一)
做点笔记,不要遗漏自己觉得重要的或者以前不知道的东西,也督促自己学下去,不要半途而废。
Tips:
- is和as操作符都会校验对象是否兼容于指定类型,强转也会检验。所以最佳搭配是as后判断是否为null,而不是is后as或is后强转。前者只会有一次校验,后者都会有两次。
- 命名空间是C#等的语言特性,CLR并不知道命名空间的任何事情。
- 类型对象中保存了类型对象指针,同步索引块,静态字段和方法(静态,虚,非虚)。方法执行时会先进行JIT编译(如果需要的话),然后调用JIT编译的代码。
- JIT编译器会根据环境生成不同的CPU指令(如x86,x64等),所以方法在第一次执行时性能损耗较大,后边会有显著提高。
- long在C#中是64位的,但在CLR的一些其他语言中不是,如(C++/CLI),所以作者倾向于使用Int32代替int,Int64代替long。但我还是习惯和喜欢后者:)
- 使用checked会检测溢出,对应的使用unchecked则不检测。默认是不检测的。对应的IL指令是add.ovf(检测)和add(不检测)。其他指令类似。
- 只有引用类型才可以加锁,值类型不可以,因为引用类型才有同步索引块(sync block index)。当然装箱的值类型可以上锁,因为它其实已经是引用类型。
- 装箱代价较大,因为有一个复制的过程,拆箱相对代价较小,只是一个引用指向改变的过程。但拆箱一般也会伴随着一次复制。
- 如下代码:
-
int a = 5; Console.WriteLine("{0}, {1}, {2}", a, a, a);
- 慎用值类型,除非满足:1.比较小,因为方法传递时会有复制开销。2.不可变的,否则在装箱拆箱并修改值的过程中可能造成不期望的错误。
- 对dynamic讲解的比较浅,有空研究下DLR的知识。
- internal是可以给其他程序集访问的!即传说中的友元程序集,可以通过加InternalsVisibleTo标签来实现。
- 静态类是不可以实例化的,生成的IL中没有.ctor方法。静态类必须直接从System.Object派生,不能实现任何接口(接口方法只能提供给实例调用),不能有实例成员,不能作为实例变量使用。
- 声明一个静态类会导致C#编译器将该类声明为abstract和sealed。
- call和callvirt的不同:后者不能使用在静态方法上。后者会有一个检测对象是否是Null的操作。
static void Test() { Program p = null; int a = p.GetFive(); } public int GetFive() { return 5; }
Test方法如果将p的callvirt改为call的话是可以运行的?
- 反射可以修改readonly字段!无所不能的反射。 在反射面前,神马修饰符都是浮云,法律已经不能阻止它了。
- Object的MemberwiseClone方法会在不调用实例构造器的前提下创建一个新的类型的实例,机制是分配内存后直接复制源对象的字节数到新对象中,无怪乎为啥它特么属于浅拷贝。
- 类型构造器(即静态构造器)是自动线程同步安全的,适合在类型构造器中初始化任何需要单例的对象。
- 类型构造器是.cctor(class constructor)。
- 自定义的显示转换或隐式转换操作符是不能通过is as调用的。
- 编译器会先去寻找类本身定义的方法列表,再去找扩展方法。所以扩展方法可能有版本问题。
- 原来很疑惑为什么Microsoft的类库中扩展方法都要判断this是否为空。其实它就是个语法糖,和普通静态方法一样。