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