代码之家  ›  专栏  ›  技术社区  ›  user306080

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

  •  1
  • user306080  · 技术社区  · 14 年前

    大家好

    我写了以下方法:

    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);
    }
    

    我收到一封信

    排队

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

    有人能指出我的错误吗?

    3 回复  |  直到 14 年前
        1
  •  1
  •   Romain Verdier    14 年前

    假设 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) => { /* */ }
    
        2
  •  4
  •   Hans Passant    14 年前

    dynamicMethod.CreateDelegate动态方法(事件信息.EventHandlerType,这个);

    这个 参数不正确。它引用了您的类,即生成动态类型的类。你肯定需要 目标对象

        3
  •  1
  •   leppie    14 年前

    打电话的时候 DynamicMethod.CreateDelegate ,不应传递目标参数。

    编辑:

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