Заставить DynamicMethod вызывать другой метод

Я хочу иметь возможность подписаться на любое событие любого объекта, передав имя события и действие в зависимости от клиентского кода. У меня есть следующий код

public static class EventSubscriber
{
    public static object Subscriber<TEventArgs>(
        this object obj,
        string eventName,
        Action handler,
        Func<TEventArgs, bool> canExecute)
    {
        var eventInfo = obj.GetType().
            GetEvent(eventName);

        if (eventInfo == null)
            throw new ArgumentException("Event name provided does not exist", nameof(eventName));

        var handlerArgs = eventInfo.EventHandlerType.
            GetMethod("Invoke").
            GetParameters()
            .Select(p => p.ParameterType).ToArray();



        var method = new DynamicMethod("method", typeof (void), handlerArgs);
        var generator = method.GetILGenerator(256);
        generator.EmitCall(OpCodes.Call, handler.Method, null);

        eventInfo.
            AddEventHandler(
                obj,
                method.CreateDelegate(eventInfo.EventHandlerType));
        return obj;
    }
}

Использование кода выше:

var Polygons = new ObservableCollection<Polygon>(myList);
Polygons.Subscriber<NotifyCollectionChangedEventArgs>
              ("CollectionChanged",
              () => MessageBox.Show("hello"),
              e => e.OldItems != null);

Это вызывает InvalidProgramException, когда событие срабатывает. Я знаю, что это сложно, и я мог бы просто подписаться, используя +=, но кто-нибудь может сказать, почему мой код дает сбой? Я предполагаю, что что-то не так с ILGenerator.Emit, какие-либо предложения?


person Andriy Shevchenko    schedule 03.12.2016    source источник


Ответы (1)


Вы забыли вернуться в конце DynamicMethod.

var method = new DynamicMethod("method", typeof (void), handlerArgs);
var generator = method.GetILGenerator(256);
generator.EmitCall(OpCodes.Call, handler.Method, null);
generator.Emit(OpCodes.Ret); //every method must have a return statement

И класс, который компилятор создает для лямбды () => MessageBox.Show("hello"), является закрытым.[ссылка]

Когда вместо этого вы используете метод public static в классе public, он работает.

var Polygons = new ObservableCollection<Polygon>(myList);
Polygons.Subscriber<NotifyCollectionChangedEventArgs>
    ("CollectionChanged",
    () => MessageBox.Show("hello"), //must in a public class and a public static method
    e => e.OldItems != null);
person NtFreX    schedule 03.12.2016