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);
}
}
}
1 Answer 1
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()
.
-
\$\begingroup\$ Does this approach really not keep open any object references to the classes which have the handler methods, then? \$\endgroup\$Dib– Dib2016年01月28日 07:48:10 +00:00Commented 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\$Dmitry Nogin– Dmitry Nogin2016年01月28日 07:57:29 +00:00Commented Jan 28, 2016 at 7:57