Вызов метода существующего объекта с использованием IL Emit - PullRequest
1 голос
/ 31 октября 2019

Я пытаюсь написать перехватчик на основе атрибутов (что-то похожее на DynamicProxy). Идея заключается в том, что на основе определенных пользовательских атрибутов будет вызываться метод внутри этого атрибута, т.е.

  • Вызов метода внутри класса атрибута до вызова фактического метода.
  • Вызов фактического методаmethod.

Я могу переопределить существующий метод, используя 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 в стек, а затемпозвоните attribute.attributeMethod(), отправив вызов на MethodInfo. Однако везде, где я смотрю, я могу найти примеры создания нового экземпляра возражения с использованием OpCodes.NewObj. Я не хочу использовать это, потому что атрибуты могут иметь параметры.

Я не могу думать ни о каком вызове метода внутри класса атрибута (который заменил бы комментарий step1).

РЕДАКТИРОВАТЬ: На основании комментариев я пытаюсь переместить GetCustomAttribute часть кода в IL. Это то, что у меня сейчас есть

il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethod));
il.Emit(OpCodes.Call, typeof(Attribute).GetMethod("GetCustomAttribute", new [] { typeof(MemberInfo), typeof(Type) }));

Это выдает ошибку для меня. Любые советы?

1 Ответ

2 голосов
/ 31 октября 2019

Динамическая генерация кода всегда раздражает. Давайте сначала приготовим несколько вспомогательных методов, чтобы избавиться от всех вещей .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 не делает 'на самом деле существует, это реализовано как метод расширения.)

...