I am trying to use GetField() in order to get to an EventHandler from a Castle-based dynamicproxy of a class implementing INotifyPropertyChanged. However:

myObject.GetType().GetField( "PropertyChanged", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance ) returns a null.

Does anyone know of any special tricks / method for getting to an EventHandler from a class / dynamic proxy of a class (and call any subscribers) via Reflection?

有帮助吗?

解决方案

Here's the sample how to get PropertyChanged event via reflection and fire it.
Should work for any type, even for Castle-based dynamic proxy. The only requirement is that PropertyChanged exists.
Code:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection;

namespace ConsoleApplication1 { internal class FooDerived : Foo { }

internal class Foo : INotifyPropertyChanged
{
    private string someProperty;

    public string SomeProperty
    {
        get { return someProperty; }
        set
        {
            someProperty = value;
            OnPropertyChanged("SomeProperty");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var instance = new FooDerived();
        instance.PropertyChanged += (sender, e) => Console.WriteLine("Property '{0}' has been changed!", e.PropertyName);

        FireEventOn(instance: instance,
                    eventName: "PropertyChanged",
                    createEventArgs: () => new PropertyChangedEventArgs(propertyName: "SomeProperty"));
    }

    // only not null reference to instance is needed
    // so will work on any not null instance
    // of any type
    private static void FireEventOn(object instance, string eventName, Func<object> createEventArgs)
    {
        BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
        FieldInfo e = GetEvent(eventName, instance.GetType(), lookupWholHierarchy: true);
        var eventDelegate = (MulticastDelegate)e.GetValue(instance);
        if(eventDelegate == null)
        {
             // nothing to call
             return;
        }

        foreach (Delegate d in eventDelegate.GetInvocationList())
        {
            Console.Write("Dynamically firing the event: ");
            d.Method.Invoke(d.Target, new[] { instance, createEventArgs() });
        }
    }

    private static FieldInfo GetEvent(string eventName, Type type, bool lookupWholHierarchy = false)
    {
        if (!lookupWholHierarchy)
        {
            return type.GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
        }
        else
        {
            foreach (var t in GetHierarchy(type))
            {
                var e = t.GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
                if (e != null)
                {
                    return e;
                }
            }
        }

        return null;
    }

    private static IEnumerable<Type> GetHierarchy(Type startFrom)
    {
        var current = startFrom;
        while (current != null)
        {
            yield return current;
            current = current.BaseType;
        }
    }


   }
}

Hope this helps.

EDIT
Updated the code: added (pretty dumb) logic for looking up for the event through the whole inheritance hierarchy.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top