动态调用代码
迄今为止我已经获得运行时对象的句柄(如类型和方法)仅作描述用例如输出它们的名称但是如何做得更多呢?如何实际调用某个方法呢?
此例的几个要点是首先从一个 MyClass mc 实例检索一个 SystemType 实例然后从该类型检索一个 MethodInfo 实例最后当调用 MethodInfo 时通过把它作为调用的第一个参数来传递将其绑定到另一个 MyClass (mc) 实例中
前面讲过对于您预期在源代码中见到的类型和对象使用之间的区别这个示例使这种区别变得模糊逻辑上您检索了一个方法的句柄然后调用该方法就象它属于一个不同的对象一样对于熟悉函数式编程语言的程序员来说这可能轻而易举;但对于只熟悉 C# 的程序员来说要分离对象实现和对象实例化可能就不是那么直观了
组合在一起
至此我已经探讨过检查和调用的基本原理接下来我会用具体的例子把它们组合在一起设想您希望交付一个库带有必须处理对象的静态帮助器函数但在设计的时候您对这些对象的类型没有任何概念!这要看函数调用方的指示看他希望如何从这些对象中提取有意义的信息函数将接受一个对象集合和一个方法的字符串描述符然后它将遍历该集合调用每个对象的方法用一些函数聚合返回值
就此例而言我要声明一些约束条件首先字符串参数描述的方法(必须由每个对象的底层类型实现)不会接受任何参数并将返回一个整数代码将遍历对象集合调用指定的方法逐步计算出所有值的平均值最后因为这不是生产代码在求和的时候我不用担心参数验证或整数溢出
在浏览示例代码时可以看到主函数与静态帮助器 ComputeAverage 之间的协议除了对象自身的通用基类之外并不依赖任何类型信息换句话说您可以彻底改变正在传送的对象的类型和结构但只要总是能使用字符串描述一个方法且该方法返回整数ComputeAverage 就可以正常工作!
需要注意的一个关键问题跟隐藏在最后这个例子中的 MethodInfo(一般反射)有关注意在 ComputeAverage 的 foreach 循环中代码只从集合中的第一个对象中抓取一个 MethodInfo然后绑定用于所有后续对象的调用正如编码所示它运行良好 — 这是 MethodInfo 缓存的一个简单例子但此处有一个根本性的局限MethodInfo 实例仅能由其检索对象同等层级类型的实例调用因为传入了 IntReturner 和 SonOfIntReturner(继承自 IntReturner)的实例才能这样运行
在示例代码中已经包含了名为 EnemyOfIntReturner 的类它实现了与其他两个类相同的基本协议但并没有共享任何常见共享类型换句话说该接口逻辑上等同但在类型层级上没有重叠要探讨 MethodInfo 在该情形下的使用请尝试向集合添加其他对象通过new EnemyOfIntReturner()得到一个实例再次运行示例您会遇到一个异常指出 MethodInfo 不能用于调用指定的对象因为它和获得 MethodInfo 时的原始类型完全无关(即使方法名称和基本协议是等同的)要使您的代码达到生产水准您需要做好遇到这一情形的准备
一个可能的解决方案可以是通过自己分析所有传入对象的类型保留对其共享的类型层级(如果有)的解释如果下一对象的类型与任意已知类型层级相异就需要获取和存储一个新的 MethodInfo另一解决方案是捕获 TargetException并重新获取一个 MethodInfo 实例这里提到的两种解决方案都各有其优缺点Joel Pobar 为本杂志 五月期写过一篇优秀的文章内容关于 MethodInfo 缓沖和我所极力推荐的反射性能
希望此示例演示的向应用程序或框架中添加反射可以为日后的自定义或可扩展性增加更多的灵活性不可否认较之本机编程语言中的同等逻辑使用反射可能会有些繁琐如果您感到对您或您的客户来说向代码中添加基于反射的后期绑定过于麻烦(毕竟他们需要以某种方式在您的框架中说明他们的类型和代码)那么可能仅需要适度的灵活性以取得某种平衡
序列化的高效类型处理
至此我们已通过若干示例讲述了 NET 反射的基本原理接下来让我们看一下现实世界中的情形如果您的软件通过 Web 服务或其他进程外远程技术与其他系统进行交互那么您很可能已经遇到序列化问题序列化本质上是将活动的占用内存的对象转变成适合线上传输或磁盘存储的数据格式
NET Framework 中的 SystemXmlSerialization 命名空间提供了拥有 XmlSerializer 的强大序列化引擎它可以使用任意托管对象并将其转换成 XML(日后也可将 XML 数据转换回类型化的对象实例这一过程称之为反序列化)XmlSerializer 类是一种强大的企业就绪的软件片断如果您在项目中面临序列化问题它将是您的首选但为了教学目的我们来探讨如何实现序列化(或者其他类似的运行时类型处理实例)
设想情形您正在交付一个框架需要使用任意用户类型的对象实例并将其转换成某种智能型数据格式例如假定有一个驻留内存的对象类型为如下所示的 Address
(pseudocode)
class Address
{
AddressID id;
String Street City;
StateType State;
ZipCodeType ZipCode;
}
如何生成适当的数据表示形式以方便日后使用?或许一个简单的文本呈现将解决这一问题
Address:
Street: Microsoft Way
City: Redmond
State: WA
Zip:
[] [] [] []