使用lambda作为方法的Delegate.CreateDelegate会产生“方法参数长度不匹配”异常

问题描述:

我遇到问题,Test1会产生“System.ArgumentException:方法参数长度不匹配”,而Test2和Test3正常传递。我需要使用反射来订阅一个事件,如果我使用简单的方法,一切都可以工作,但是当我进入lambdas时,它会按预期停止工作。使用lambda作为方法的Delegate.CreateDelegate会产生“方法参数长度不匹配”异常

调试显示所有lambda表示它们是“无效<> m__0(Int32)”,这是事件的正确类型,与“eventInfo.EventHandlerType”相同。

为什么会失败?或者,也许,如何解决这个问题?

做c#添加更多的参数,由lambda创建的方法,就像在Test1中一样?

::完整的代码在这里:

public class A 
{ 
    public void Test1() 
    { 
     var str = "aa"; 
     B.Subscribe(typeof(C), "myEvent", (int a) => { var any = str; }, null); 
    } 

    public void Test2() 
    { 
     B.Subscribe(typeof(C), "myEvent", (int a) => { var any = a; }, null); 
    } 

    public void Test3() 
    { 
     B.Subscribe<int>(typeof(C), "myEvent", callback, this); 
    } 

    public void callback(int a) { } 
} 

public static class B 
{ 
    public static void Subscribe<T>(Type type, string eventName, Action<T> callback, object target) 
    { 
     var eventInfo = type.GetEvent(eventName, BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static); 
     var handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, target, callback.Method); 
     eventInfo.AddEventHandler(null, handler); 
    } 

} 

public sealed class C 
{ 
    public static event Action<int> myEvent; 
} 

编辑:

显然,这是单声道的bug。 GetInvocationList()[0]获取Delegate解决了上面例子中的问题。

但订阅事件会产生“System.InvalidCastException:无法从源类型转换为目标类型”。如果事件的类型是动作但自定义委托的不是:(如果“C”类就是这样,它抛出,如果“C”类是像上面,它通过细)

public sealed class C 
{ 
    public static event MyDel myEvent; 
    public delegate void MyDel(int a); 
} 

有没有什么不同的问题?编辑#2,事件期望MyDel类型,但获取Action Int32。我如何将Action<T>转换为MyDel或更好的转换为eventInfo.EventHandlerType,因为我不知道可以有什么类型的事件。

+1

适用于MS .NET。可能是一些单声道问题? – VinSmile

+0

似乎错误的单声道错误。检查http://lists.ximian.com/pipermail/mono-bugs/2010-December/106824.html –

+0

如果使用GetInvocationList()(即返回列表的第一个元素)而不是'Method',会发生什么? ?后者应该可以工作,但正如@VinSmile所说,也许是单声道问题。 –

其实在进一步调查后我发现我的target不好。

对于在类中定义的方法,将类实例作为目标是可以的。 对于lambda表达式,我认为它是空的,至少它与null,一起工作,只要它不影响创建lambda表达式的方法内部定义的局部变量。

所以行动有一个属性目标,使用callback.TargetDelegate.CreateDelegate解决了这个问题。

lambda的目标实际上持有对类实例和它接触的所有本地变量(调试器显示它)的引用。

奇怪的是,它在最新的.NET上工作,也许单声道和.NET之间的细微差别。