Using using to unbind event handlers

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());
		}
	}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s