Raise an event via reflection in c#

2019-03-29 03:33发布

问题:

I want to write a reusable function to raise an event via reflection.

After searching, I found this similar question: How do I raise an event via reflection in .NET/C#?

It works until I register an event handler to WinForm control and try to invoke it. The private field '<EventName>' simply disappears.

Below is my simplified code which reproduces the problem:

Program.cs:

public static void Main()
{
    Control control = new Control();
    control.Click += new EventHandler(control_Click);

    MethodInfo eventInvoker = ReflectionHelper.GetEventInvoker(control, "Click");
    eventInvoker.Invoke(control, new object[] {null, null});
}

static void control_Click(object sender, EventArgs e)
{
    Console.WriteLine("Clicked !!!!!!!!!!!");
}

Here is my ReflectionHelper class:

public static class ReflectionHelper
{
    /// <summary>
    /// Gets method that will be invoked the event is raised.
    /// </summary>
    /// <param name="obj">Object that contains the event.</param>
    /// <param name="eventName">Event Name.</param>
    /// <returns></returns>
    public static MethodInfo GetEventInvoker(object obj, string eventName)
    {
        // --- Begin parameters checking code -----------------------------
        Debug.Assert(obj != null);
        Debug.Assert(!string.IsNullOrEmpty(eventName));
        // --- End parameters checking code -------------------------------

        // prepare current processing type
        Type currentType = obj.GetType();

        // try to get special event decleration
        while (true)
        {
            FieldInfo fieldInfo = currentType.GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetField);

            if (fieldInfo == null)
            {
                if (currentType.BaseType != null)
                {
                    // move deeper
                    currentType = currentType.BaseType;
                    continue;
                }

                Debug.Fail(string.Format("Not found event named {0} in object type {1}", eventName, obj));
                return null;
            }

            // found
            return ((MulticastDelegate)fieldInfo.GetValue(obj)).Method;
        }
    }

Additional information:

  • Event in same class: worked.
  • Event in different class, sub-class in same assembly: worked.
  • Event in MY different assembly, debug & release mode: worked.
  • Event in WinForm, DevExpress, ...: did not work

Any help is appreciated.

回答1:

Events in WinForms are generally overridden and not don't have a one-to-one delegate backing. Instead the class (basically) has a dictionary of the event->delegate mappings and the delegates are only created when the events are added. So you can't assume there's a delegate backing the event once you access the field with reflection.

Edit: this falls prey to the same problem, but is better than getting it as a field and casting it.

  var eventInfo = currentType.GetEvent(eventName); 
  var eventRaiseMethod = eventInfo.GetRaiseMethod()
  eventRaiseMethod.Invoke()


回答2:

Here's some code I had laying around. 'obj' is the object for which you want to invoke a method, and 'methodName' is the method that you want to invoke:

public void Invoke(Object obj, String methodName) {
    MethodInfo m = obj.GetType().GetMethod(methodName);

    if (m != null) {
        m.Invoke(obj, null);
    }
}

Example usage:

String s = " test string ";
Invoke(s, "Trim");

I haven't tested this across assemblies, but the project I took it from was tested and it worked great.