Home > OS >  Add and remove async event handlers using reflection in c#
Add and remove async event handlers using reflection in c#

Time:12-09

I have the following code sample that actually works:

namespace DeviceLib
{
    public interface IInstrument
    {
        event Action<InstrumentConnectionStatus> OnConnectionChanged;
        event Action OnRemoteMeasureRequested;
    }

public class InstrumentInstance
{
    public delegate Task EventCompletedHandler(object sender, dynamic eventArgs);
    public event EventCompletedHandler StatusChanged = async delegate { };
    public event EventCompletedHandler RemoteMeasureRequested = async delegate { };
    IInstrument Instrument;

    public InstrumentInstance(string DriverName)
    {
        Instrument = DriverName switch
        {
            Instrument1.DRIVER => new Instrument1Driver(),
            instrument2.DRIVER => new Instrument2Driver(),
            _ => throw new NotSupportedException($"Driver {DriverName} not supported"),
        };
    }

    public async Task<object> OnStatusChanged()
    {
        Instrument.OnConnectionChanged  = async (InstrumentConnectionStatus status) =>
        {
            await StatusChanged(nameof(OnStatusChanged), status.ToString());
        };

        return null;
    }

    public async Task<object> OnRemoteMeasureRequested()
    {
        Instrument.OnRemoteMeasureRequested  = async () =>
        {
            await RemoteMeasureRequested(nameof(OnRemoteMeasureRequested), null);
        };

        return null;
    }
}

}

namespace DeviceConsumer 
{
    public class InstrumentService
    {
        static Type Type = typeof(DeviceLib.InstrumentInstance);
        DeviceLib.InstrumentInstance InstrumentInstance;
        static List<MethodInfo> AvailableEventHandlers =  Type.GetMethods().Where(x => x.DeclaringType == Type && !x.IsSpecialName && x.Name.StartsWith("On")).ToList();
        static List<EventInfo> AvailableEvents=Type.GetEvents().ToList();
    

        public InstrumentService()
        {

        }

        public async Task CreateInstrumentInstance(string driverName)
        {
            this.InstrumentInstance = new DeviceLib.InstrumentInstance(driverName);
        
        
            // Invoking the methods that wrap the event handlers with reflection
            foreach (MethodInfo eventHandler in AvailableEventHandlers)
                await (Task<object>)eventHandler.Invoke(InstrumentInstance, new object[] { });


            InstrumentInstance.StatusChanged  =  async(s,e) => await ProcessInstrumentEvent(s,e);
            InstrumentInstance.RemoteMeasureRequested  = async (s, e) => await ProcessInstrumentEvent(s, e);
        }

        private async Task ProcessInstrumentEvent(object sender, dynamic eventArgs)
        {
            await Task.Run(() =>
            {
                Console.Write($"Event {sender} Fired!");
            });
        }

}

 

Now I want to substitute the static part that associates the events to the ProcessInstrumentEvent method:

InstrumentInstance.StatusChanged  =  async(s,e) => await ProcessInstrumentEvent(s,e);
InstrumentInstance.RemoteMeasureRequested  = async (s, e) => await ProcessInstrumentEvent(s, e);

With something like:

        foreach (EventInfo ev in AvailableEvents)
        {
           // EventHandler handler = async delegate (object s, dynamic e) { await ProcessInstrumentEvent(s, e); };
          
           //  EventHandler handler = new EventHandler(async (s, e) => await ProcessInstrumentEvent(s,e));


             ev.AddEventHandler(InstrumentInstance, handler);

        }

None of the 2 approaches to define "handler" worked, where I'm failing here? I think I'm very close, the goal of this implementation is to dynamically add (and later remove) the handlers that target the method "ProcessInstrumentEvent" in class "InstrumentService" so without using = and -= operators, so I thought that I could achieve that using the reflection methods "AddEventHandler" and "RemoveEventHandler"

CodePudding user response:

This should work:

var obj = new InstrumentInstance("foo");

EventCompletedHandler handler = async (s, e) => await ProcessInstrumentEvent(s, e);

foreach (var evt in obj.GetType().GetEvents(BindingFlags.Public | BindingFlags.Instance))
{
    if (evt.EventHandlerType == typeof(EventCompletedHandler))
    {
        Console.WriteLine($"Binding '{evt.Name}'");
        evt.AddEventHandler(obj, handler);
    }
}

It should also work without the extra indirection:

EventCompletedHandler handler = ProcessInstrumentEvent;
  • Related