EventInfo.AddEventHandler - bug or not?

Reflection is the process by which a computer program can observe and modify its own structure and behavior. The programming paradigm driven by reflection is called reflective programming. [Wikipedia]

Working a few days ago on a plugin architecture on .NET Framework I’ve found something very interesting. The method System.Reflection.EventInfo.AddEventHandler does late-binding. You’ll probably say “so what?”. Let’s see the problem with an example:

Using Visual Studio 2005/2008, create two C# projects (a windows forms application project and a class library one). Rename the class from the class library project to “TestClass.cs”.

We are going to dynamically load the dll and add his public methods that begin with “Method” to respond to some menu item clicks.

First we need the dll code. Let’s add three methods (don’t forget to add a reference to System.Windows.Forms because we’ll show some message boxes):

public class TestClass
{
    public static void Method1()
    {
MessageBox.Show("Method1");
}

    public static void Method2()
    {
        MessageBox.Show("Method2");
    }

    public static void Method3()
    {
        MessageBox.Show("Method3");
    }
}

That’s it, we have finished with the class library :-)

Now go the the windows forms project, in Form1.cs and add a MenuStrip (name it “menuStrip”) and on this add one MenuItem (named “menu1″).

It’s time to write some code that loads the dll. First add the needed using, “using System.Reflection”, and then, on the Load event of Form1 write:

Assembly classLib = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "ClassLibrary.dll");
Type t = classLib.GetType("ClassLibrary.TestClass");

Ok, now we have the assembly loaded and we want to subscribe with each of it’s methods that begin with “Method” to a menu item click. Let’s find the methods that begin with “Method”, create a menu item and subscribe to the event, using AddEventHandler, with a delegate that invokes the method from dll.

foreach (MethodInfo method in t.GetMethods())
{
    if (method.Name.IndexOf("Method") == 0)
    {
        ToolStripItem newMenuItem = menu1.DropDownItems.Add(method.Name)       
        EventInfo e = newMenuItem .GetType().GetEvent("Click");
        e.AddEventHandler(newMenuItem, new EventHandler(delegate(object sender, EventArgs ev)
            {
                method.Invoke(null, null);
            }
        ));
    }
}

The code compiles, and our form shows with the menu we wanted :D (oh yeah!!).

Let’s click a Method1 item… Huh? Exception… Something is strange. I’ve subscribed with static methods and I receive “Non-static method requires a target.”.

Just adding a watch on “method” reveals the truth… I’ve subscribed with “GetHashCode()” which is not a static method. Probably my fault - bad code. Add a break point where the method is added to the delegate and surprise: “Method1()”. When I first saw this I was so confused. I didn’t knew if this is a bug, my fault or something normal. Searching on MSDN revealed the “supreme” truth:

All actions are performed using late binding.

This way I’ve found out that you cannot subscribe directly to an event with a set of methods and get the right result. If you watch every delegate from the event above you’ll see that all of them are pointing to “GetHashCode()” even though you subscribed with something else.

Is it a bug or not?

Technorati Tags: , , , , ,

One Response to “EventInfo.AddEventHandler - bug or not?”

  1. Good Samaritean Says:

    This code migth help you: it is not due to the late binding you have problems, it’s due to the fact that you make the delegate inline and the functionality it’s runned in the for context which remains at the GetHashCode method ;)

    Here is a simple solution:
    foreach (MethodInfo method in t.GetMethods())
    {
    if (method.Name.IndexOf(”Method”) == 0)
    {
    ToolStripItem newMenuItem = menu1.DropDownItems.Add(method.Name);
    //Anonymous method and event subscribe

    newMenuItem.Click += delegate(object s, EventArgs ev)
    {
    //method.Invoke(null, null);
    t.GetMethod(((ToolStripDropDownItem)s).Text).Invoke(null, null);
    };
    }
    }

Leave a Reply

Please write the reply in English!