C# 为什么动态创建事件处理程序时会出现参数异常?

C# 为什么动态创建事件处理程序时会出现参数异常?,c#,reflection,event-handling,C#,Reflection,Event Handling,大家好 我写了以下方法: private void RegisterEvent(object targetObject, string eventName, string methodName) { EventInfo eventInfo = targetObject.GetType().GetEvent(eventName); MethodInfo method = eventInfo.EventHandlerType.GetMethod("Invoke"); IEnu

大家好

我写了以下方法:

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);
}
有人能指出我的错误吗


提前感谢。

调用
DynamicMethod.CreateDelegate
时,不应传递目标参数

编辑:


我认为您还必须将第一个参数设为0,并相应地更改codegen。

调用
DynamicMethod.CreateDelegate
时,不应传递目标参数

编辑:

我认为您还必须使第一个参数=0,并相应地更改codegen

dynamicMethod.CreateDelegate(eventInfo.EventHandlerType,this)

这个论点不可能是正确的。它引用您的类,即生成动态类型的类。当然,您需要的是targetObject

dynamicMethod.CreateDelegate(eventInfo.EventHandlerType,this)


这个论点不可能是正确的。它引用您的类,即生成动态类型的类。当然,您需要使用targetObject。

假设
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);
}
编辑:

因为可以使用.NET3.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约定的事件(发送方+事件参数)。通过这种方式,我们可以利用逆变并始终传递以下类型的lambda:

(object sender, EventArgs e) => { /* */ }

假设
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);
}
编辑:

因为可以使用.NET3.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约定的事件(发送方+事件参数)。通过这种方式,我们可以利用逆变并始终传递以下类型的lambda:

(object sender, EventArgs e) => { /* */ }

您从哪里获得异常(在哪一行)以及消息是什么?感谢您的回答,我得到了“错误绑定到目标方法”。在这一行中,Delegate methodDelegate=dynamicMethod.CreateDelegate(eventInfo.EventHandlerType,this);您从哪里获得异常(在哪一行)以及消息是什么?感谢您的回答,我得到了“错误绑定到目标方法”。在这一行中,Delegate methodDelegate=dynamicMethod.CreateDelegate(eventInfo.EventHandlerType,this);谢谢,它很有效。如果methodName不是静态方法(即,它应该修改某些属性),我应该如何修改该方法?如果该方法不是静态的,则在推送arg0(或arg1)并执行调用之前,需要在堆栈上推送QueryWindow的实例。这可能会有问题,因为您肯定必须将此实例作为动态方法的参数传递。如果您在问题中发布有关此特定场景的更多详细信息,我可以相应地更新我的答案。再次感谢您的帮助。此想法是使用EventArgs中存储的数据更新RichTextBox。有要调用的方法(尝试在不添加静态的情况下使其工作):public void PrintMessage(object obj){MethodInfo MethodInfo=obj.GetType().GetProperty(“Message”).getMethod();段落=新段落();段落.Inlines.Add(MethodInfo.Invoke(obj,null).ToString());//MessageBox.Show(methodInfo.Invoke(obj,null).ToString());box.Document.Blocks.Add(段落);}是否仅限于.NET2.0?RegisterEvent方法位于何处?在QueryWindow中?是的,RegisterEvent位于QueryWindow中。我并不局限于.NET2.0。如果需要的话,我可以使用3.5或4.0谢谢,它可以工作。如果methodName不是静态方法(即,它应该修改某些属性),我应该如何修改该方法?如果该方法不是静态的,则在推送arg0(或arg1)并执行调用之前,需要在堆栈上推送QueryWindow的实例。这可能会有问题,因为您肯定必须将此实例作为动态方法的参数传递。如果您在问题中发布有关此特定场景的更多详细信息,我可以相应地更新我的答案。再次感谢您的帮助。此想法是使用EventArgs中存储的数据更新RichTextBox。有要调用的方法(尝试在不添加静态的情况下使其工作):public void PrintMessage(object obj){MethodInfo MethodInfo=obj.GetType().GetProperty(“Message”).getMethod();段落=新段落();段落.Inlines.Add(MethodInfo.Invoke(obj,null).ToString());//MessageBox.Show(methodInfo.Invoke(obj,null).ToString());box.Document.Blocks.Add(段落);}是否仅限于.NET2.0?RegisterEvent方法位于何处?在QueryWindow中?是的,RegisterEvent位于QueryWindow中。我并不局限于.NET2.0。如有必要,我可以使用3.5或4.0