示例6.8 通过MethodInfo.Invoke显式地调用
Public sealed class Utils
{
Static public int callAdd(object target){
//从对象的类型中抓取”Add”方法
Type type=target.GetType();
MethodInfo method=type.GetMethod(“Add”,BindingFlags.Public| BindingFlags.NonPublic| BindingFlags.Instance);
//检查方法是否存在
If(method==null) return 0;
//形成参数值列表
Object[] args=new object[]{10,230,30};
//执行这个方法
Object result=method.Invoke(target,args);
//返回结果
Return (int)result;
}
}
图6.5展示了MethodInfo对象如何关联底层方法和目标对象。注意有一个底层System.RuntimeMehtodHandle引用,它指向描述该方法的CLR托管数据结构。你可以使用System.RuntimeMethodHandle.GetFunctionPointer方法访问底层方法代码的地址,当找到这个地址后,喜欢使用底层编程技巧的程序员就能够直接调用这个方法,而不用承担MethodInfo.Invoke的开销。
由GetFunctionPointer返回的地址意味着它必须由CIL的calli指令调用。与call和calivirt指令不一样,calli指令直接将目标方法的元数据记号编码到指令流中。在运行时, calli指令所期望的目标方法的地址将被压入到堆栈中。这种间接性允许CLR支持c风格函数指针。例如,假设你有如下的C#类型声明:
Public class Target{
Public static int Addc(int x,int y){return x+y;}
Pulbic int Subtract(int x,int y){return x-y;}
}
你将能编写下面的C++代码:
Using namespace System;
Using namespace System::Reflection;
Typedef int (__fastcall*AddProc)(int,int);
Typedef int (__fastcall*SubProc)(Target*,int ,int);
Void f(Target*pTarget){
//获取方法指针
Type*ptype=pTarget- >GetType();
MethodInfo*padd=ptype- >GetMethod(S’”Add”);
MethodInfo*psub=ptype- > GetMethod(S”Subtract”);
IntPtr pfnAdd=padd- >MethodHandle.GetFunctionPointer();
IntPtr pfnSub=psub- > MethodHandle.GetFunctionPointer();
//调用
Int r1=((AddProc)pfnAdd)(3,4);
Int r2=((SubProc)pfnSub)(pTarget,5,6);
}
遗憾的是,在NET framework l.0版本下,C++编译器的CLR兼容模式(/CLR)并不支持使用_fastcall堆栈规则的函数指针声明,它通常是由CLR内部所使用的规则。尽管构造合适的IA-32机器代码是可能的.C++还是禁止在托管方法中内联程序集。这导致程序员只能使用ILASM编写必要的CIL用于调用函数,除此之外别无选择。这里,ILASM是随.NET framework SDK一起发布的CIL汇编器。
下面的ILASM方法声明展示了如何调用前面例子中所展示的Add方法.
.method public hidebysig static int 32
Call(native int pfn,int32 x,int32 y)cil managed{
.maxstack 3
ldarg.1 //压入x
ldarg.2 //压入y
ldarg.0 //压入目标方法的地址
calli int 32(int 32, int 32)
ret
}
如果c++编译器支持__fastcall函数指针,则这个方法产生的机器代码将与c++函数指针产生的机器代码相同。
为了调用实例方法Subtract,你可以使用这个ILASM方法
.method public hidebysig static int 32
Call(native int pfn,object pThis,int32 x,int32 y)cil managed{
.maxstack 4
ldarg.1 //压入pThis.
ldarg.2 //压入x
ldarg.3 //压入y
ldarg.0 //压入目标方法的地址
calli int 32(object, int 32, int 32)
ret
}
在这两种情形下,Call的第一个参数都是一个由MethodBase.GatFunctionPointer所返回的函数指针。注意,在这两个例子中很重要的是,当JIT编译器把这个CIL翻译成机器代码时.似乎每个参数都被传递到堆栈,但是根据__fistcall的调用约定,前两个参数将被传递到ecx和
edx寄存器。W嶯C