清晰的组件化目标是否因在库间共享过多类型信息而落空?或许您需要高效的强类型化数据存储但如果每次对象模型发展后都需要更新您的数据库架构那会耗费很大成本所以您更愿意在运行时推断出其类型架构吗?您需要交付能接受任意用户对象的组件并以某种智能化的方式处理它们吗?您希望库的调方者能以编程方式向您说明它们的类型吗?
如果您发现自己在苦苦维持强类型化数据结构的同时又冀望于最大化运行时灵活性那么您大概会愿意考虑反射以及它如何改善您的软件在本专栏中我将探讨 Microsoft NET Framework 中的 SystemReflection 命名空间以及它如何为您的开发体验提供助益我将从一些简单的示例开始最后将讲述如何处理现实世界中的序列化情形在此过程中我会展示反射和 CodeDom 如何配合工作以有效处理运行时数据
在深入探究 SystemReflection 之前我想先讨论一下一般的反射编程首先反射可定义为由一个编程系统提供的任何功能此功能使程序员可以在无需提前了解其标识或正式结构的情况下检查和操作代码实体这部分内容很多我将逐一展开说明
首先反射提供了什么呢?您能用它做些什么呢?我倾向于将典型的以反射为中心的任务分为两类检查和操作检查需要分析对象和类型以收集有关其定义和行为的结构化信息除了一些基本规定之外通常这是在事先不了解它们的情况下进行的(例如在 NET Framework 中任何东西都继承自 SystemObject并且一个对象类型的引用通常是反射的一般起点)
操作利用通过检查收集到的信息动态地调用代码创建已发现类型的新实例或者甚至可以轻松地动态重新结构化类型和对象需要指出的一个要点是对于大多数系统在运行时操作类型和对象较之在源代码中静态地进行同等操作会导致性能降低由于反射的动态特性因此这是个必要的取捨不过有很多技巧和最佳做法可以优化反射的性能(有关优化使用反射的更多深入信息请参见 msdnmicrosoftcom/msdnmag/issues///Reflection)
那么什么是反射的目标呢?程序员实际检查和操作什么呢?在我对反射的定义中我用了代码实体这个新术语以强调一个事实从程序员的角度来说反射技术有时会使传统对象和类型之间的界限变得模糊例如一个典型的以反射为中心的任务可能是
从对象 O 的句柄开始并使用反射获得其相关定义(类型 T)的句柄
检查类型 T获得它的方法 M 的句柄
调用另一个对象 O(同样是类型 T)的方法 M
请注意我在从一个实例穿梭到它的底层类型从这一类型到一个方法之后又使用此方法的句柄在另一个实例上调用它 — 显然这是在源代码中使用传统的 C# 编程技术无法实现的在下文中探讨 NET Framework 的 SystemReflection 之后我会再次通过一个具体的例子来解释这一情形
某些编程语言本身可以通过语法提供反射而另一些平台和框架(如 NET Framework)则将其作为系统库不管以何种方式提供反射在给定情形下使用反射技术的可能性相当复杂编程系统提供反射的能力取决于诸多因素程序员很好地利用了编程语言的功能表达了他的概念吗?编译器是否在输出中嵌入足够的结构化信息(元数据)以方便日后的解读?有没有一个运行时子系统或主机解释器来消化这些元数据?平台库是否以对程序员有用的方式展示此解释结果?
如果您头脑中想象的是一个复杂的面向对象类型的系统但在代码中却表现为简单的C 语言风格的函数而且没有正式的数据结构那么显然您的程序不可能动态地推断出某变量 v 的指针指向某种类型 T 的对象实例因为毕竟类型 T 是您头脑中的概念它从未在您的编程语句中明确地出现但如果您使用一种更为灵活的面向对象语言(如 C#)来表达程序的抽象结构并直接引入类型 T 的概念那么编译器就会把您的想法转换成某种日后可以通过合适的逻辑来理解的形式就象公共语言运行时 (CLR) 或某种动态语言解释器所提供的一样
反射完全是动态运行时的技术吗?简单的说不是这样整个开发和执行周期中很多时候反射对开发人员都可用且有用一些编程语言通过独立编译器实现这些编译器将高级代码直接转换成机器能够识别的指令输出文件只包括编译过的输入并且运行时没有用于接受不透明对象并动态分析其定义的支持逻辑这正是许多传统 C 编译器的情形因为在目标可执行文件中几乎没有支持逻辑因此您无法完成太多动态反射然而编译器会不时提供静态反射 — 例如普遍运用的 typeof 运算符允许程序员在编译时检查类型标识
[] [] [] []