I have a problem, Test1 produces "System.ArgumentException : method argument length mismatch" while Test2 and Test3 passes fine. I need to subscribe to an event using reflection, everything works if I use simple methods, but when I get into lambdas, it stops working as expected.
Debugging shows for all lambdas that they are "Void <>m__0(Int32)" which is the correct type for event and the same as "eventInfo.EventHandlerType".
Why does it fail? Or maybe, how to workaround that?
Do c# add more arguments to a method which is created by lambda like in Test1?
::Complete code here:
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;
}
EDIT:
Apparently it's Mono bug. GetInvocationList()[0] to get the Delegate fixes the issue in above example.
But subscribing to event produces "System.InvalidCastException : Cannot cast from source type to destination type." If event is not of type Action but of custom delegate: (if class "C" is like that, it throws, if class "C" is like above, it passes fine)
public sealed class C
{
public static event MyDel myEvent;
public delegate void MyDel(int a);
}
Is it different issue? Edit #2, event expects MyDel type, but gets Action Int32. How can I convert from Action<T> to MyDel or better, to eventInfo.EventHandlerType, because I don't know what type of event there can be.
Actually, after further investigation I noticed that my target was bad.
For methods defined in class, class instance as target is okay. For lambdas I thought it was null, at least it works with null, as long as it does not interfere with local variables defined inside the method where the lambda was created.
So Action has a property Target, using callback.Target in Delegate.CreateDelegate solves the issue.
The target for lambda actually holds a reference to the class instance and to the all local variables it touches (debugger shows it).
Strange that it worked on newest .NET, maybe a slight difference between mono and .NET.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With