Dynamically binding event handlers is a common source of bugs, and memory leaks.
The traditional approach is to use a finally block:
EventHandler eventHandler = (s, e) => fooService.Bar(); eventSource.AnEvent += eventHandler; try { // do something that raises the event } finally { eventSource.AnEvent -= eventHandler; }
An alternative way to ensure that your handler is always unbound correctly is to (ab)use IDisposable (again!) and use the using sugar:
using (_eventHandlerFactory.Attach(_eventSource, _fooService)) { // do something that raises the event }
public class EventHandlerFactory { public IDisposable Attach(IEventSource eventSource, IFooService fooService) { EventHandler eventHandler = (s, e) => fooService.Bar(); eventSource.AnEvent += eventHandler; return new EventHandlerCookie(eventSource, eventHandler); } private class EventHandlerCookie : IDisposable { private readonly IEventSource _eventSource; private readonly EventHandler _eventHandler; public EventHandlerCookie(IEventSource eventSource, EventHandler eventHandler) { _eventSource = eventSource; _eventHandler = eventHandler; } public void Dispose() { _eventSource.AnEvent -= _eventHandler; } } } public interface IFooService { void Bar(); } public interface IEventSource { event EventHandler AnEvent; } [TestFixture] public class EventHandlerFactoryTests { private IEventSource _eventSource = MockRepository.GenerateStub<IEventSource>(); private IFooService _fooService = MockRepository.GenerateStub<IFooService>(); private EventHandlerFactory _eventHandlerFactory; [SetUp] public void SetUp() { _eventHandlerFactory = new EventHandlerFactory(); } [Test] public void Event_handler_is_attached() { _eventHandlerFactory.Attach(_eventSource, _fooService); _eventSource.Raise(es => es.AnEvent += null, _eventSource, EventArgs.Empty); _fooService.AssertWasCalled(fs => fs.Bar()); } [Test] public void Event_handler_is_detached_when_disposed() { using (_eventHandlerFactory.Attach(_eventSource, _fooService)) { _eventSource.Raise(es => es.AnEvent += null, _eventSource, EventArgs.Empty); } _eventSource.Raise(es => es.AnEvent += null, _eventSource, EventArgs.Empty); _fooService.AssertWasCalled(fs => fs.Bar(), o => o.Repeat.Once()); } }