问题
I am attempting to write an attribute based interceptor (something similar to DynamicProxy
). The idea being, based on certain custom attributes, a method inside that attribute would be called, i.e.
- Call a method inside attribute class before actual method is called.
- Call the actual method.
I am able to override existing method using MethodBuilder
and TypeBuilder
. However, I can't figure out how to call the method inside the Attribute.
My code :
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);
}
}
What I think I should do is load attribute
onto stack, then call attribute.attributeMethod()
by emitting a call to the MethodInfo
. However, everywhere I look, I can find examples of creating a new instance of the objected using OpCodes.NewObj
. I don't want to use this, because attributes may have parameters.
I can't think of any of calling the method inside attribute class (which would replace step1 comment).
EDIT: Based on comments, I'm trying to move the GetCustomAttribute
part of the code in IL. This is what I have right now
il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethod));
il.Emit(OpCodes.Call, typeof(Attribute).GetMethod("GetCustomAttribute", new [] { typeof(MemberInfo), typeof(Type) }));
It throws an error for me. Any tips?
回答1:
Dynamic code generation is always kind of annoying. Let's cook up some helper methods first to get rid of all the .GetMethod
stuff:
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;
}
Now let's say we have ProxyMethodAttribute
and its method MyMethod
-- here's how we'd retrieve that and call it:
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()));
Note how we need to call GetXXXFromHandle
to produce actual instances from the metadata tokens generated, and how we need a more involved call to Attribute.GetCustomAttribute
(MethodInfo.GetCustomAttribute
doesn't actually exist, this is implemented as an extension method.)
来源:https://stackoverflow.com/questions/58640074/calling-a-method-of-existing-object-using-il-emit