Skip to content

Instantly share code, notes, and snippets.

@Pondidum
Created April 22, 2012 23:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pondidum/2467463 to your computer and use it in GitHub Desktop.
Save Pondidum/2467463 to your computer and use it in GitHub Desktop.
public class EventDistributor
{
private readonly Dictionary<Type, List<WeakReference>> _events;
private static readonly Object Padlock = new Object();
public Distributor()
{
_events = new Dictionary<Type, List<WeakReference>>();
}
public void RegisterFor<TEvent>(Action<TEvent> handler)
{
var type = typeof(TEvent);
List<WeakReference> recipients;
lock (Padlock)
{
if (!_events.TryGetValue(type, out recipients))
{
recipients = new List<WeakReference>();
_events[type] = recipients;
}
recipients.Add(new WeakReference(handler));
}
}
public void UnRegisterFor<TEvent>(Action<TEvent> handler)
{
var type = typeof(TEvent);
List<WeakReference> recipients;
if (_events.TryGetValue(type, out recipients))
{
lock (Padlock)
{
recipients.RemoveAll(o => o.Target.GetHashCode() == handler.GetHashCode());
}
}
}
public void Publish<TEvent>(TEvent @event)
{
var type = typeof(TEvent);
List<WeakReference> recipients;
if (!_events.TryGetValue(type, out recipients))
{
return;
}
lock (Padlock)
{
recipients.RemoveAll(wr => wr.IsAlive == false);
recipients.ForEach(wr =>
{
var handler = (Action<TEvent>)wr.Target;
handler.BeginInvoke(@event, handler.EndInvoke, null);
});
}
}
}
[TestFixture]
public class EventDistributorTests
{
[Test]
public void When_publishing_an_event_without_a_handler()
{
var distributor = new EventDistributor();
Assert.DoesNotThrow(() => distributor.Publish(new PersonSavedEvent()));
}
[Test]
public void When_publishing_an_event_with_a_handler()
{
var wasCalled = false;
var distributor = new EventDistributor();
distributor.RegisterFor<TestEvent>(e => wasCalled = true);
distributor.Publish(new TestEvent());
Assert.IsTrue(wasCalled, "The target was not invoked.");
}
[Test]
public void When_publishing_an_event_and_un_registering()
{
var callCount = 0;
var increment = new Action<TestEvent>(e => callCount++);
var distributor = new EventDistributor();
distributor.RegisterFor<TestEvent>(increment);
distributor.Publish(new TestEvent());
distributor.UnRegisterFor<TestEvent>(increment);
distributor.Publish(new TestEvent());
Assert.AreEqual(1, callCount);
}
[Test]
public void When_the_handling_class_does_not_call_unregister()
{
var count = 0;
var increment = new Action(() => count++);
var distributor = new EventDistributor();
using (var l = new Listener(distributor, increment))
{
distributor.Publish(new PersonSavedEvent());
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
distributor.Publish(new PersonSavedEvent());
Assert.AreEqual(1, count, "OnPersonSaved should have only been called 1 time, was actually {0}", count);
}
private class TestEvent { }
public class Listener : IDisposable
{
private readonly Action _action;
public Listener(EventDistributor events, Action action)
{
_action = action;
events.RegisterFor<PersonSavedEvent>(OnPersonSaved);
}
private void OnPersonSaved(PersonSavedEvent e)
{
_action.Invoke();
}
public void Dispose()
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment