使用 IL Emit 调用现有对象的方法
Calling a method of existing object using IL Emit
我正在尝试编写一个基于属性的拦截器(类似于 DynamicProxy
)。这个想法是,基于某些自定义属性,将调用该属性内的方法,即
- 在调用实际方法之前调用属性 class 中的方法。
- 调用实际方法。
我可以使用 MethodBuilder
和 TypeBuilder
覆盖现有方法。但是,我不知道如何调用属性内部的方法。
我的代码:
static void CreateMethods<T>(TypeBuilder tb)
{
foreach (var methodToOverride in typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
var attribute = (ProxyMethod)methodToOverride.GetCustomAttribute(typeof(ProxyMethod));
if (attribute == null)
continue;
MethodBuilder methodBuilder = tb.DefineMethod(
methodToOverride.Name,
MethodAttributes.Public
| MethodAttributes.HideBySig
| MethodAttributes.NewSlot
| MethodAttributes.Virtual
| MethodAttributes.Final,
CallingConventions.HasThis,
methodToOverride.ReturnType,
Type.EmptyTypes
);
ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldstr, "The I.M implementation of C"); //step1
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //step1
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, methodToOverride);
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(methodBuilder, methodToOverride);
}
}
我认为我应该做的是将 attribute
加载到堆栈上,然后通过发出对 MethodInfo
的调用来调用 attribute.attributeMethod()
。但是,无论我在哪里看,我都能找到使用 OpCodes.NewObj
创建对象的新实例的示例。我不想用这个,因为属性可能有参数。
我想不出任何在属性 class 中调用方法的方法(这将取代 step1 注释)。
编辑:根据评论,我正在尝试移动 IL 中的 GetCustomAttribute
部分代码。这就是我现在拥有的
il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethod));
il.Emit(OpCodes.Call, typeof(Attribute).GetMethod("GetCustomAttribute", new [] { typeof(MemberInfo), typeof(Type) }));
它为我抛出一个错误。有什么建议吗?
动态代码生成总是有点烦人。让我们先编写一些辅助方法来摆脱所有 .GetMethod
东西:
static class Method {
public static MethodInfo Of<TResult>(Expression<Func<TResult>> f) => ((MethodCallExpression) f.Body).Method;
public static MethodInfo Of<T>(Expression<Action<T>> f) => ((MethodCallExpression) f.Body).Method;
public static MethodInfo Of(Expression<Action> f) => ((MethodCallExpression) f.Body).Method;
}
现在假设我们有 ProxyMethodAttribute
及其方法 MyMethod
-- 以下是我们如何检索并调用它:
il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Call, Method.Of(() => MethodBase.GetMethodFromHandle(default(RuntimeMethodHandle))));
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethodAttribute));
il.Emit(OpCodes.Call, Method.Of(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))));
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Call, Method.Of(() => Attribute.GetCustomAttribute(default(MemberInfo), default(Type), default(bool))));
il.Emit(OpCodes.Callvirt, Method.Of((ProxyMethodAttribute a) => a.MyMethod()));
请注意我们需要如何调用 GetXXXFromHandle
以从生成的元数据标记中生成实际实例,以及我们如何需要对 Attribute.GetCustomAttribute
进行更复杂的调用(MethodInfo.GetCustomAttribute
实际上并不存在,这是作为扩展方法实现的。)
我正在尝试编写一个基于属性的拦截器(类似于 DynamicProxy
)。这个想法是,基于某些自定义属性,将调用该属性内的方法,即
- 在调用实际方法之前调用属性 class 中的方法。
- 调用实际方法。
我可以使用 MethodBuilder
和 TypeBuilder
覆盖现有方法。但是,我不知道如何调用属性内部的方法。
我的代码:
static void CreateMethods<T>(TypeBuilder tb)
{
foreach (var methodToOverride in typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
var attribute = (ProxyMethod)methodToOverride.GetCustomAttribute(typeof(ProxyMethod));
if (attribute == null)
continue;
MethodBuilder methodBuilder = tb.DefineMethod(
methodToOverride.Name,
MethodAttributes.Public
| MethodAttributes.HideBySig
| MethodAttributes.NewSlot
| MethodAttributes.Virtual
| MethodAttributes.Final,
CallingConventions.HasThis,
methodToOverride.ReturnType,
Type.EmptyTypes
);
ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldstr, "The I.M implementation of C"); //step1
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //step1
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, methodToOverride);
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(methodBuilder, methodToOverride);
}
}
我认为我应该做的是将 attribute
加载到堆栈上,然后通过发出对 MethodInfo
的调用来调用 attribute.attributeMethod()
。但是,无论我在哪里看,我都能找到使用 OpCodes.NewObj
创建对象的新实例的示例。我不想用这个,因为属性可能有参数。
我想不出任何在属性 class 中调用方法的方法(这将取代 step1 注释)。
编辑:根据评论,我正在尝试移动 IL 中的 GetCustomAttribute
部分代码。这就是我现在拥有的
il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethod));
il.Emit(OpCodes.Call, typeof(Attribute).GetMethod("GetCustomAttribute", new [] { typeof(MemberInfo), typeof(Type) }));
它为我抛出一个错误。有什么建议吗?
动态代码生成总是有点烦人。让我们先编写一些辅助方法来摆脱所有 .GetMethod
东西:
static class Method {
public static MethodInfo Of<TResult>(Expression<Func<TResult>> f) => ((MethodCallExpression) f.Body).Method;
public static MethodInfo Of<T>(Expression<Action<T>> f) => ((MethodCallExpression) f.Body).Method;
public static MethodInfo Of(Expression<Action> f) => ((MethodCallExpression) f.Body).Method;
}
现在假设我们有 ProxyMethodAttribute
及其方法 MyMethod
-- 以下是我们如何检索并调用它:
il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Call, Method.Of(() => MethodBase.GetMethodFromHandle(default(RuntimeMethodHandle))));
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethodAttribute));
il.Emit(OpCodes.Call, Method.Of(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))));
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Call, Method.Of(() => Attribute.GetCustomAttribute(default(MemberInfo), default(Type), default(bool))));
il.Emit(OpCodes.Callvirt, Method.Of((ProxyMethodAttribute a) => a.MyMethod()));
请注意我们需要如何调用 GetXXXFromHandle
以从生成的元数据标记中生成实际实例,以及我们如何需要对 Attribute.GetCustomAttribute
进行更复杂的调用(MethodInfo.GetCustomAttribute
实际上并不存在,这是作为扩展方法实现的。)