C# cannot dynamically generate this simple event handler

1.6k Views Asked by At

I'm trying to learn a bit about dynamically generating event handlers and I'm having difficulty trying to recreate this simple situation:

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

// This is the event handler that I want to create dynamically
public void DoSomething(object a, object b)
{
    DoSomethingElse(a, b);
}

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

I've used reflector to generate the IL for the DoSomething method, and it gives me:

.method public hidebysig instance void DoSomething(object a, object b) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: ldarg.2 
    L_0003: call instance void MyNamespace::DoSomethingElse(object, object)
    L_0008: ret 
}

So, I've written the following code to dynamically generate and execute a method equivalent to DoSomething(...):

public void CreateDynamicHandler()
{
    var eventInfo = GetType().GetEvent("SomethingHappened");
    var eventHandlerType = eventInfo.EventHandlerType;

    var dynamicMethod = new DynamicMethod("DynamicMethod", null, new[] { typeof(object), typeof(object) }, GetType());
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = GetType().GetMethod("DoSomethingElse", new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(eventHandlerType);
    emitted.DynamicInvoke("hello", "world");
}

However, when I run this I get an InvalidProgramException: JIT Compiler encountered an internal limitation.

Can anyone point out where I've gone wrong?

[EDIT] As several people have commented, the whole IL generation thing is unnecessary if I know all of the types involved. My reason for doing this is that this is the first step towards dynamically generating event handlers at runtime for events where I do not know all the types involved. Basically I'd been following the example at http://msdn.microsoft.com/en-us/library/ms228976.aspx, got stuck, and then tried to unwind things into a simple example that I could get working.

2

There are 2 best solutions below

4
On BEST ANSWER

It is unclear why you would want to create this method dynamically. I can’t really think of any situation in which you couldn’t just apply a lambda to the event:

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

// If the signature exactly matches the delegate, just use the method name
SomethingHappened += DoSomethingElse;

public void DoSomethingDifferent(object a)
{
    Console.WriteLine("Yay! " + a);
}

// Otherwise, just use a lambda expression
SomethingHappened += (a, b) => DoSomethingDifferent(a);

That said, the reason your code doesn’t work is because DynamicMethod generates only static methods. Therefore, the IL code is invalid because Ldarg_0 and Ldarg_1 load the two parameters but Ldarg_2 refers to a non-existent parameter. If I change it in the following way, it works as one would expect — it is now a static method with three parameters, where the first parameter is basically this:

public void CreateDynamicHandler()
{
    var dynamicMethod = new DynamicMethod("DynamicMethod", null,
        new[] { typeof(MyClass), typeof(object), typeof(object) }, typeof(MyClass));
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = typeof(MyClass).GetMethod("DoSomethingElse",
        new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(
        typeof(Action<MyClass, string, string>));
    emitted.DynamicInvoke(this, "Hello", "World");
}

Replace “MyClass” with the name of your class.

Regarding the EDIT of your question, you don’t need to generate a dynamic method by writing IL code in order to call a method dynamically at runtime. Just use Reflection, for example:

public void DoSomething(object a, object b)
{
    var method = GetType().GetMethod("DoSomethingElse", BindingFlags.Instance | BindingFlags.Public);
    method.Invoke(this, new object[] { a, b });
}

or:

// Note “static”
public static void DoSomething(dynamic instance, object a, object b)
{
    // This will call whatever “DoSomethingElse” method exists on the type
    // that “instance” has *at run-time*
    instance.DoSomethingElse(a, b);
}
1
On

How about this scenario...

You have pairs of Methods and events such that each pair is MethodAsync and MethodCompleted. Each MethodCompleted has a different signature (differing in the subtype of the eventargs, which is the second parameter).

You want to create a wrapper for calling a given MethodAsync that hooks an all-purpose eventhandler up to the corresponding MethodCompleted.

Normally, you could create a method "void GlobalHandler(object, object)", and do

MethodCompleted += GlobalHandler;  

However, you cannot pass the event object, so you have to use reflection to get a reference to the event handler and then do AddHandler. Again, the snag is that AddHandler doesn't like polymorphism (it seems), and complains that MethodCompleted wants something other than GlobalHandler.

In such a case, it seems like you'd need to create a DynamicMethod of the EXACT signature that MethodCompleted expects, and then call GlobalHandler from that. Am I right, or am I also missing something here.