【发布时间】:2023-03-08 00:01:01
【问题描述】:
我正在尝试编写基于属性的拦截器(类似于DynamicProxy)。这个想法是,基于某些自定义属性,将调用该属性内的方法,即
- 在调用实际方法之前调用属性类中的方法。
- 调用实际方法。
我可以使用MethodBuilder 和TypeBuilder 覆盖现有方法。但是,我不知道如何调用 Attribute 中的方法。
我的代码:
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 创建对象的新实例的示例。我不想用这个,因为属性可能有参数。
我想不出任何调用属性类中的方法(这将取代 step1 注释)。
编辑:基于 cmets,我试图在 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) }));
这对我来说是一个错误。有什么建议吗?
【问题讨论】:
-
您还需要发出加载属性的代码,因为您不能将属性实例传递给生成的代码。请记住,当该代码运行时您的代码不会,因此只能直接传递独立于您的代码的简单原语和元数据,而不是本地创建的对象。 (由于属性是从元数据实例化的,理论上可以以某种方式发出实例化它所需的数据而不是获取属性,但我认为这没有任何好处。)
-
你能举个例子吗?如果我将代码的属性部分移至 IL,我仍然需要将
typeof(ProxyMethod)放在堆栈顶部。我也不知道该怎么做。 -
这更容易:如果您调查编译器为此类代码(例如使用 LINQPad)发出的 IL,您会发现您需要一个
ldtoken操作码。 (事实上,通过研究此方法的编译代码,您可以了解如何在生成的代码中移动它的一部分。此外,尽可能依赖调用非生成代码,例如在众所周知的静态方法中,作为您需要生成更容易的代码。) -
@JeroenMostert 你能看看编辑吗?
标签: c# reflection .net-core cil