Почему я получаю исключение Argument при динамическом создании обработчика событий? - PullRequest
1 голос
/ 02 сентября 2010

Добрый день всем

Я написал следующий метод:

private void RegisterEvent(object targetObject, string eventName, string methodName)
{
    EventInfo eventInfo = targetObject.GetType().GetEvent(eventName);
    MethodInfo method = eventInfo.EventHandlerType.GetMethod("Invoke");
    IEnumerable<Type> types = method.GetParameters().Select(param => param.ParameterType);

    DynamicMethod dynamicMethod = new DynamicMethod(eventInfo.EventHandlerType.Name, typeof (void), types.ToArray(), typeof (QueryWindow));
    MethodInfo methodInfo = typeof (QueryWindow).GetMethod(methodName, new[] { typeof (object) });

    ILGenerator ilGenerator = dynamicMethod.GetILGenerator(256);
    ilGenerator.Emit(OpCodes.Ldarg_1);
    ilGenerator.EmitCall(OpCodes.Call, methodInfo, null);

    dynamicMethod.DefineParameter(1, ParameterAttributes.In, "sender");
    dynamicMethod.DefineParameter(2, ParameterAttributes.In, "e");

    // Get an argument exception here
    Delegate methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType, this);
    eventInfo.AddEventHandler(targetObject, methodDelegate);
}

Я получаю ArgumentException с сообщением

Ошибка привязки к целевому методу.

в строке

Delegate methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType, this); 

Может ли кто-нибудь указать на мою ошибку?

Заранее спасибо.

Ответы [ 3 ]

4 голосов
/ 02 сентября 2010

dynamicMethod.CreateDelegate (eventInfo.EventHandlerType, this);

Аргумент this не может быть правильным. Это относится к вашему классу, который генерирует динамический тип. Конечно, вам нужно targetObject вместо.

1 голос
/ 02 сентября 2010

Предполагая, что methodName является статическим методом QueryWindow, это должно работать:

private static void RegisterEvent(object targetObject, string eventName, string methodName)
{
    var eventInfo = targetObject.GetType().GetEvent(eventName);
    var method = eventInfo.EventHandlerType.GetMethod("Invoke");
    var types = method.GetParameters().Select(param => param.ParameterType);

    var methodInfo = typeof(QueryWindow).GetMethod(methodName, new[] { typeof(object) });

    // replaced typeof(void) by null      
    var dynamicMethod = new DynamicMethod(eventInfo.EventHandlerType.Name, null, types.ToArray(), typeof(QueryWindow));

    ILGenerator ilGenerator = dynamicMethod.GetILGenerator(256);

    // Using Ldarg_0 to pass the sender to methodName ; Ldarg_1 to pass the event args
    ilGenerator.Emit(OpCodes.Ldarg_0);
    ilGenerator.EmitCall(OpCodes.Call, methodInfo, null);

    // Added return
    ilGenerator.Emit(OpCodes.Ret); 

    // Removed parameter definition (implicit from DynamicMethod constructor)

    // Removed the target argument
    var methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType);
    eventInfo.AddEventHandler(targetObject, methodDelegate);
}

Редактировать:

Поскольку вы можете использовать .NET 3.5, вы должны создать дерево выражений. Вот еще одно решение:

public class QueryWindow
{
    public void RegisterEvent(object targetObject, string eventName, string methodName)
    {
        var eventInfo = targetObject.GetType().GetEvent(eventName);
        var sender = Expression.Parameter(typeof (object), "sender");
        var e = Expression.Parameter(typeof (EventArgs), "e");
        var body = Expression.Call(Expression.Constant(this), methodName, null, e);
        var lambda = Expression.Lambda(eventInfo.EventHandlerType, body, sender, e);
        eventInfo.AddEventHandler(targetObject, lambda.Compile() );
    }

    public void OnEvent(object o)
    {
        Console.WriteLine(o);
    }
}

Обратите внимание, что метод OnEvent больше не является статическим. Я также предполагаю, что события, на которые вы пытаетесь подписаться, являются событиями, которые следуют соглашениям .NET (отправитель + аргументы событий). Таким образом, мы можем использовать контравариантность и всегда передавать лямбду типа:

(object sender, EventArgs e) => { /* */ }
1 голос
/ 02 сентября 2010

При вызове DynamicMethod.CreateDelegate не следует передавать целевой параметр.

Редактировать:

Я думаю, вам также нужно было бы сделать первый параметр = 0 и соответственно изменить кодовый код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...