Learning CLR via C#(一)

做点笔记,不要遗漏自己觉得重要的或者以前不知道的东西,也督促自己学下去,不要半途而废。

Tips:

  1. is和as操作符都会校验对象是否兼容于指定类型,强转也会检验。所以最佳搭配是as后判断是否为null,而不是is后as或is后强转。前者只会有一次校验,后者都会有两次。
  2. 命名空间是C#等的语言特性,CLR并不知道命名空间的任何事情。
  3. 类型对象中保存了类型对象指针,同步索引块,静态字段和方法(静态,虚,非虚)。方法执行时会先进行JIT编译(如果需要的话),然后调用JIT编译的代码。
  4. JIT编译器会根据环境生成不同的CPU指令(如x86,x64等),所以方法在第一次执行时性能损耗较大,后边会有显著提高。
  5. long在C#中是64位的,但在CLR的一些其他语言中不是,如(C++/CLI),所以作者倾向于使用Int32代替int,Int64代替long。但我还是习惯和喜欢后者:)
  6. 使用checked会检测溢出,对应的使用unchecked则不检测。默认是不检测的。对应的IL指令是add.ovf(检测)和add(不检测)。其他指令类似。
  7. 只有引用类型才可以加锁,值类型不可以,因为引用类型才有同步索引块(sync block index)。当然装箱的值类型可以上锁,因为它其实已经是引用类型。
  8. 装箱代价较大,因为有一个复制的过程,拆箱相对代价较小,只是一个引用指向改变的过程。但拆箱一般也会伴随着一次复制。
  9. 如下代码:
  10. int a = 5;
    Console.WriteLine("{0}, {1}, {2}", a, a, a);
     
    会装箱三次,即会在堆中生成三份完全一样的对象,浪费时间和内存。如果有这种情况,先对a装箱再将引用对象当做参数传递,或a.ToString后将字符串当参数。
  11. 慎用值类型,除非满足:1.比较小,因为方法传递时会有复制开销。2.不可变的,否则在装箱拆箱并修改值的过程中可能造成不期望的错误。
  1. 对dynamic讲解的比较浅,有空研究下DLR的知识。
  2. internal是可以给其他程序集访问的!即传说中的友元程序集,可以通过加InternalsVisibleTo标签来实现。
  3. 静态类是不可以实例化的,生成的IL中没有.ctor方法。静态类必须直接从System.Object派生,不能实现任何接口(接口方法只能提供给实例调用),不能有实例成员,不能作为实例变量使用。
  4. 声明一个静态类会导致C#编译器将该类声明为abstract和sealed。
  5. call和callvirt的不同:后者不能使用在静态方法上。后者会有一个检测对象是否是Null的操作。
static void Test()
{
    Program p = null;
    int a = p.GetFive();
}

public int GetFive()
{
    return 5;
}

Test方法如果将p的callvirt改为call的话是可以运行的?

  1. 反射可以修改readonly字段!无所不能的反射。 在反射面前,神马修饰符都是浮云,法律已经不能阻止它了。
  2. Object的MemberwiseClone方法会在不调用实例构造器的前提下创建一个新的类型的实例,机制是分配内存后直接复制源对象的字节数到新对象中,无怪乎为啥它特么属于浅拷贝。
  3. 类型构造器(即静态构造器)是自动线程同步安全的,适合在类型构造器中初始化任何需要单例的对象。
  4. 类型构造器是.cctor(class constructor)。
  5. 自定义的显示转换或隐式转换操作符是不能通过is as调用的。
  6. 编译器会先去寻找类本身定义的方法列表,再去找扩展方法。所以扩展方法可能有版本问题。
  7. 原来很疑惑为什么Microsoft的类库中扩展方法都要判断this是否为空。其实它就是个语法糖,和普通静态方法一样。
posted @ 2011-04-19 23:26  我就是砖家  阅读(289)  评论(1编辑  收藏  举报