显式方法调用
前面的讨论是关于虚方法如何在调用点( call site)与执行的实际方法之间引入一层间接。这层间接性对于凋用方是非常透明的,CLR使用目标对象的具体引用自动地确定使用哪个方法。除了虚方法,CLR还提供了更灵活的方法调用功能,你可以发现并调用任意的方法,而不需要对它们的签名和方法名字有任何先验的了解。这个功能[显式方法调用 (explicit method invocation)]对于构建高度动态的系统是很关键的。
我们不妨回顾一下,对于CLR元数据,可以通过System.Type及其相关类库进行访问。System.Type的功能之一就是发现一个给定类型的方法。System.Reflection.MethodInfo类型公开了方法的元数据。如第4章中所描述的,MethodInto类型使方法的签名为可用的,包括参数的类型和名字。而MethodInfo类型对于调用底层方法的能力我们还没有讨论到。事实
上,它通过MethodInf.Invoke方法公开了这个功能。
MethodInfo Invoke具有两个重载版本,其中较为复杂的一个允许调用方提供映射码(mapping code),以处理参数类型不匹配和重载解析。这个版本的MetthodInfo.Invoke方法主要被用于在动态地类型化语言中支持管道( plumbing).这已经超出了本书的范闱,两个方法中较为简单的版本假定调用方提供了底层方法期望出现的参数。示例6.7展示了两种方法的原型。
示例6.7 System.Reflection.MethodInfo.Invoke
using System;
using System.Globalization;
namespace System.Reflection{
public abstract class MethodInfo:MethodBase{
public virtual object Invoke(object target,BindingFlags invokeAttr,Binder binder,object[] args,CultureInfo culture);
public virtual object Invoke(object target,object[] args);
}
}
要使用MethodInfo.Invoke的简单版木形式,需要提供两个参数.图6.5展示了这种用法。第一个参数是目标列象的一个引用。如果底层方法被声明为static,那么这个引用将被忽略。如果底层方法不是static.则这个引用必须指向一个与MethodInfo反射类型相兼容类型的对象.如果给这个参数传递了一个与该类型不兼容的对象,MeLhodInfo.Invoke就将抛出一个System.Reflaction.TargetException异常。
MethodInfo.Inoke的第二个参数接收一个object类型引用的数组,每个参数对应一个数组元素。这个数组的长度必须匹配参数所期望的个数。数组中每个引用对象的类型必须与对应参数的类型是类型兼容的。如果有任何一个不符合,Methodinfo.Invoke都将抛出Systen.Reflection.TargetParameterCountException异常或者System.ArgumentException异常。
MethodTnfo.Invoke的实现将使用所提供的参数值和对象引用来调用底层方法。为了完成这一点,MethodInfo.Invoke将形成一个堆栈帧(stack frame),它是基于CLR之下的底层方法声明和处理程序架构的,然后,MethodTnfo.Invoke从对象引用的数组中将参数值拷贝到这个堆栈上,当堆栈帧完全形成后,MethodInfo.Invoke就对目标方法发出一个处理程序相关的调用(例如,IA-32中的Call)。当方法执行完成后,MethodInfo.Invoke将接着识别任何通过引用传递的参数,并把它们拷贝回参数值的当前数组中。最后,如果这个方法返回一个值,则该值将被作为Methodinfo.Invoke调用的结果而返回。
示例6.8展示了一个c#程序,它在一个任意的对象上调用一个名为“Add”的方法。这段代码假定对象的底层类型由有一个Add方法。此外,这个例子还假定Add方法恰好接收三个System.Int32类型参数,并且,底层方法将返回一个System.Int32值。顺便说一下,这个例子使用了BindingFlags.NonPublic标志,标明非公有方法将会被考虑。这个功能允许你避开方法的访问修饰符(例如,private);然而,只有受信任的代码才能违反这种封装。0轛骮