欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表
最新评论

第四章:用类型编程-运行时的类型(1)

创建时间:2012年09月03日 17:29  阅读次数:(5533)
分享到:

第3章阐述了CLR的公共类型系统( common type system.CTS),其重点是类型的静态定义,并假定类型在运行程序中的使用可能是隐式的(这句话的意思是,假定类型在运行时的使用是绝对的,并且类型的创作者——开发人员完全依赖于CLR制定的默认南侧规,而没有对这些关型进行显式地编程,以控制它们在应用程序和支撑结构的上下文的状态,——译者注)。到了本章,我们将重点讲述把类型深度集成到应用程序和支撑结构的显式编程技巧。

运行时的类型
类型本身并不是万能的。类型真正有意思的地方在于,程序员使用类型的实例,并让它们相互作用。类型的实例( instance)既可以是对象,也可以是值,这取决于类型是如何定义的。基本数据类型( primitive type)的实例是值,而绝人多数用户定义类型的实例是对象,尽管也存在能够产生值的类型,相关内容将在第5章详细说明。

每个对象和每一个值都是一个确切类型的实例,实例和类型的从属关系通常足隐式的。例如,声明一个System.Int32类型的变量或字段,其结果是分配一块符合该类型的内存,它的存在与否则取决于操纵它的执行代码。CLR(和基于CLR的编译器)只允许对那些属于System.Int.32类型定义的值进行操作。实施这种值和System.Int32娄型的从属关系并不需要额外的开销,因为编译器会在编详时去实施,并且CLR验证器(CLR通过实现CTS的严格类型验证和代码验证基础结构来加强代码可靠性。此外,代码验证还包括对代码访问安全的管理(参见第9章、第10章)——译者注)会确保在代码加载后维护这种从属关系。

每个对象也属于个类型。然而,因为对象总是通过对象引用来访问的,所以,被引用对象的实际类型可能不匹配该引用的声明类型。当对象引用的是一个abstract类型时,就是这样的情形。显然,我们需要某种机制来明确对象与其类型的从属关系,以处理这种情形。接下来让我们分析CLR的对象头( ohjecl header)(从前面两章的学习中,我们已经了解到:在NET中,类型的确切的内存分配是不透明的。因此,作者这里的对象头实际是一种设想,读者可在将它理解为认识CLR内存机制的辅助数据结构.事实上-在Microsoft官方公布的资料中找不到这个数据结构的资料。---译者注)。

CLR的每个对象都以一个同定大小的对象头开始,如图4.1所示。对象头不能通过程序直接访问,但它确实存在。对象头的确切格式没有正式的文档说明,因而,下面的描述是住IA-32体系结构上对 CLR l.0版本进行实验分析推断而来的。CLI的其他几种实现在某种程度上也像是由这个格式衍生而来的。
对象头有两个字段:它的第一个字段是同步块( sync block)索引。你可以使用这个字段推迟该对象与附加资源的关联(例如,锁、COM对象);对象头的第二个字殷是一个句柄( handle)它指向一个不透明的数据结构,用于表示该对象的类型。尽管这个句柄的位置也没有正式文档说明,但通过System.RuntimeTypeHandle类型可以对它显式地支持。有趣的是,在CLR的当前实现中,对象引用总是指向对象头的类型句柄字段。第—个用户自定义类型的字段总是sizeof(Void)字节,不管对象引用指向哪里。


图4.1

给定类型的每个实例在对象头中都会有同样的类型句柄值,类型句柄(type handle)简而言之是指向一个非透明的、无正式文档说明的数据结构的指针。这个数据结构包含了类型的完整描述,以及一个指向类型元数据的内存表示的指针。数据结构的内容对于各种关键性能的操作(例如,虚方法的分发、对象分配)在某种意义上做了优化处理,使之尽可能地快。有关该数据结构的第一个应用就是动态类型强制转换( dynamic type coericon)。    当从一个对象引用的类型转换到另一个对象引用的类型时,必须考虑两个类型之间的关系。如果初始引用的类型被认定与新引用的类型兼容,那么,CLR所要做的转换只是一个简单的IA-32mov指令。这通常出现于这样的赋值情形中:当从一个派生类型的引用到一个直接或问接基类型的引用,或者到一个已知兼容的接口的引用(这就是向上类型转换,up-casting).另一方面,如果初始引用的类型与新引用的类型之间的兼容性不是已知的,那么,CLR必须执行一个运行时测试,以确定对象的类型与所需要的类型是否是兼容的。当赋值是从一个基类型或接口引用到一个深度派生的类型的引用时(这就是向下类型转换,down-casting),或者到一个互不相关的类型的引用时(平行类型转换, side-casting),这种测试总是必需的。

为了支持向下类型转换和平行类型转换,CIL(公共中间语言,Commonintermediate Language)定义了两个操作码:isinst和castclass。这两个操作码分别带有两个参数:一个对象引用和一个用于表示所期望的引用类型的元数据标记。两个操作码都会生成代码,用于检查对象的类型句柄,以决定刘象的类型是否与请求的类型相兼容。两个操作码的不同之处在于它们报告测试结果的方式。如果测试成功,两个操作码只是简单地将对象引用保留在用于测试计算的堆栈上:如果测试失败,则两个操作码的表现各不相同。如果请求的娄型不被支持cagtclass操作码会抛出一个System.invalidCastexception类型的异常。相反,如果测试失败,isinst操作码只是简单地把一个空引用放到用于测试计算的堆栈上。由于异常的开销相对较高,所以只有当转换总是期望成功时,才使用产生castClass操作码的构件。相似地,如果两种结果都是预期的,则应该使用产生isinsl操作码的构件。自趣的是,JIT编译器将CIL的isinst利castclass指令分别翻译为对内部的JIT—Isinstanceof函数和JIT_ChkCast函数的调用,这两个函数都没有文档说明。不过,在后面的讨论中,我们将把这两个缺乏文档说明的函数的行为,解释为前面淡到的文档化的CIL指令( 5作者的意思是,尽管完成实际的类型转换是通过调用这两个底层函数(JIT_isLnstanceof函数和JIT_ChkCast函数)实现的,但我们只考虑它们的上一层,即相应的中间语言指令isinst和castclass指令。细心的读者可以通过NET Framework的工具ILDASM.EXE重现这两条指令。一译者注)。

注:因为本章是从图片上识别过来再编辑的,难免有些地方会有错误,如果各位看到有难以理解的字,请不要纠结单个字的意思,应结合上下文来理解。

u
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 27+69=? 的结果(结果是:96)
结  果: