4
\$\begingroup\$

Given a class which is an event dispatcher whose underlying store is a dictionary of events whose key is the event type private readonly Dictionary<Type, Delegate> _applicationEventHandlers;, is this the correct way to remove all remaining delegates when the class needs to be disposed?

    private void RemoveAllListeners()
    {
        foreach (Type handlerType in _applicationEventHandlers.Keys)
        {
            Delegate handlers = _applicationEventHandlers[handlerType];
            Delegate.RemoveAll(handlers, handlers);
        }
    }

Or is there a better way? I have included the whole class below for clarity.

using System;
using System.Collections.Generic;


internal class ApplicationEventDispatcher : IApplicationEventDispatcher, IDisposable
{
    private bool _disposed;
    private readonly Dictionary<Type, Delegate> _applicationEventHandlers;

    public ApplicationEventDispatcher()
    {
        _applicationEventHandlers = new Dictionary<Type, Delegate>();
    }

    ~ApplicationEventDispatcher()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // free other managed objects that implement IDisposable only
            RemoveAllListeners();
        }

        // release any unmanaged objects
        // set the object references to null

        _disposed = true;
    }


    public void AddListener<T>(ApplicationEventHandlerDelegate<T> handler) where T : IApplicationEvent
    {
        if (_applicationEventHandlers.ContainsKey(typeof(T)))
        {
            Delegate handlersForType = _applicationEventHandlers[typeof(T)];
            _applicationEventHandlers[typeof(T)] = Delegate.Combine(handlersForType, handler);
        }
        else
        {
            _applicationEventHandlers[typeof(T)] = handler;
        }
    }

    public void RemoveListener<T>(ApplicationEventHandlerDelegate<T> handler) where T : IApplicationEvent
    {
        if (_applicationEventHandlers.ContainsKey(typeof(T)))
        {
            var handlerToRemove = Delegate.Remove(_applicationEventHandlers[typeof(T)], handler);
            if (handlerToRemove == null)
            {
                _applicationEventHandlers.Remove(typeof(T));
            }
            else
            {
                _applicationEventHandlers[typeof(T)] = handlerToRemove;
            }
        }
    }

    private void RemoveAllListeners()
    {
        foreach (Type handlerType in _applicationEventHandlers.Keys)
        {
            Delegate handlers = _applicationEventHandlers[handlerType];
            Delegate.RemoveAll(handlers, handlers);
        }
    }

    public void Dispatch(IApplicationEvent @event)
    {
        if (@event == null)
        {
            return;
        }

        if (_applicationEventHandlers.ContainsKey(@event.GetType()))
        {
            _applicationEventHandlers[@event.GetType()].DynamicInvoke(@event);
        }
    }
}
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Delegate.RemoveAll has no effect on existing delegates. It actually constructs a new one. Delegates are immutable and cannot be changed after construction.

You can remove this line. I would not implement IDisposable or any cleanup code here at all. Just release reference on event dispatcher (it makes sense to do it anyway after calling Dispose().

\$\endgroup\$
2
  • \$\begingroup\$ Does this approach really not keep open any object references to the classes which have the handler methods, then? \$\endgroup\$
    – Dib
    Commented Jan 28, 2016 at 7:48
  • \$\begingroup\$ As long as your event dispatcher is not referenced by variables in stack or pointed by static variables, directly or indirectly - references via delegates it keeps will not be counted by GC to prevent event handler objects from garbage collecting. Release your last reference to event dispatcher and you are good. \$\endgroup\$ Commented Jan 28, 2016 at 7:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.