Search code examples
c#reflectioninotifypropertychangedcastle-dynamicproxyeventhandler

How to get an EventHandler from a Castle-based DynamicProxy (and call subscribers if any) via Reflection


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?


Solution

  • 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.