Search code examples
c#eventsevent-handlingweakeventmanager

Is it possible to write a class that hides the difference between adding WeakEventManager handlers and traditional ones?


I have a C# (.NET 4.6.1) project that uses a lot of events. I'm tempted to move all the event handlers to the new WeakEventManager pattern - to avoid endlessly worrying about deregistering handlers to avoid memory leaks.

However I have to do lots of testing for performance and want a way to switch between the two methods easily. So far I've been using conditional compilation to do this along the lines of :

#if WeakEvents
    WeakEventManager<EventSource,EArgs>.AddHandler(source, "TheEvent", handler);
#else
    source.TheEvent += handler;
#endif

This works, but its messy. Ideally I'd like to create a class that hides this away. ie create a class which internally can use either method. Then I can change all my source to attach handlers with the new class and switch easily (or even move to some new method in the future).

However I can't see how to write that class - because I can't pass the event as a parameter - and there would have to be some reflection going on with the handler/name which is beyond me.

Is there an easy way to do that?


Solution

  • Here is one way to do that:

    static class EventHelper {
        public static void Subscribe<TSource, TEventArgs>(TSource source, Expression<Func<TSource, EventHandler<TEventArgs>>> eventRef, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            var memberExp = eventRef.Body as MemberExpression;
            if (memberExp == null)
                throw new ArgumentException("eventRef should be member access expression");
    
            var eventName = memberExp.Member.Name;
        #if WeakEvents
            WeakEventManager<TSource, TEventArgs>.AddHandler(source, eventName, handler);            
        #else
            // some reflection here to get to the event
            var ev = source.GetType().GetEvent(eventName);
            if (ev == null)
                throw new ArgumentException($"There is no event with name {eventName} on type {source.GetType()}");
            ev.AddMethod.Invoke(source, new object[] { handler });
        #endif
        }
    }
    

    Usage is as simple as:

    // second parameter is event reference
    EventHelper.Subscribe(source, s => s.TheEvent, Handler);