C#如何识别引用的真实类型(一)——转载



知识储备,如果您已经十分熟习这些,可以跳过

1 什么是引用?

 引用是一个数据结构,包含了一个计算机内存堆地址的值,就类似C++中的指针一样,本文中所有出现有关"引用"字句,读者都可以把它理解成C,C++中的指针



  再说一遍,引用 与 指针 是不同的,例如 GC在回收内存的时候,会修改引用的值,但本文的重点并不是讲述 引用 与 指针的差别,所以,读者可以把所有在这里出现的 "引用" 理解成 c,c++ 中的指针

2 栈 与 堆, .net中的内存分配?

  http://blog.csdn.net/cuike519/archive/2009/12/23/5063333.aspx ,如果你还不是很清楚这些,这里有你所需要了解的

方法是类的,非静态字段是实例的

  类中的方法(无论是静态的,还是非静态的)本质上都是属于类的,类中非静态的字段,是属于类的实例的

  正是因为这一点的不同,类中的方法代码只创建了一份在内存中,而类中的非静态字段是随着类的实例创建而创建的

  理论上 ,实例一个类的对象,只需要在内存中分配 与 类中所有非静态字段总和大小 一致的内存就可以了

  然后把分配的堆中内存地址的首地址 赋给 栈 中的引用

  但,实际情况并非如此简单,在.net 中,当实例化一个类的时候, 总共分配了( sizeof(void) + 类中所有非静态字段大小总和 )  的内存

  那么,那多出的 sizeof(void
) 是用来做什么的呢?

P.S.  如果我没有记错的话 在32位的CPU下, sizeof(void) 的大小是4个字节

那多出来的4字节,是做什么的呢?

 引用指向了一个内存地址,在该地址 + sizeof(void
) 后便紧跟着实例的非静态字段

 每个类都拥有 sizeof(void*) 大小的"多余"内存,那么在那里面,究竟有什么呢?

 在那里面,总共有两个东西

 第一个是名为 syncblock 的索引

 第二个是指向一个为公开的数据结构的句柄

 不要小看那个未公开的数据结构,那里有实际类型的相关数据,.net正是依靠它来准确识别引用的真实类型,就算你按照以下的例子做,CLR也会知道,谁到底是谁

  Subclass sub = new Subclass();

  BassClass B = (BassClass)sub;

  B.GetType(); // CLR说,别装B了,我知道你是 subclass 类的实例

真像已经大白,还想要知道更多?

  

   1, 那个未公开的数据结构,有一个名字,叫做 CORINFO_CLASS_STRUCT(这个名字只是做参考,实际中,也许并不是这个名字),我们无法使用编程的方法直接访问这个数据结构,但是.net提供了一个类, System.RuntimeTypeHandle 允许我们用已经定义好的方法,访问它

  有关 System.RuntimeTypeHandle 的更多信息,请参照MSDN

   

   2, MSIL提供了两个进行类型间相互转换的IL指令

    isinst

    castclass

    这两个指令在编译成本机代码的时候,都会产生访问 CORINFO_CLASS_STRUCT以验证是否可以进行转换

    它们唯一的不同是当验证失败时,castclass 抛出一个 System.InvalidCastException异常 (很熟习吧?)

    isint 则会将一个为 null 的引用添加到栈顶 (MSIL是一门基于栈的语言,有兴趣的童鞋,可以google下 :-))

    在C#中

    as ,is 操作符 被转换成 isinst

   

     () 强制类型转换符 被转换成     castclass