Skip to content

Instantly share code, notes, and snippets.

@nathan130200
Last active April 1, 2020 23:27
Show Gist options
  • Save nathan130200/0f7a6a8dbbae1083abe6a7d9e7fc8adf to your computer and use it in GitHub Desktop.
Save nathan130200/0f7a6a8dbbae1083abe6a7d9e7fc8adf to your computer and use it in GitHub Desktop.
Thread-safe event handler system in C# using dynamic delegate
using System.Collections.Generic;
namespace System.Event
{
public class EventHandlerList
{
private class DelegateBinder
{
public readonly object Key;
private List<Delegate> Handlers;
public DelegateBinder(object key)
{
this.Key = key;
this.Handlers = new List<Delegate>();
}
public void Register(Delegate item)
{
lock (this.Key)
this.Handlers.Add(item);
}
public int Count()
=> this.GetHandlers().Count;
public void Unregister(Delegate item)
{
lock (this.Key)
this.Handlers.Remove(item);
}
public IReadOnlyList<Delegate> GetHandlers()
{
Delegate[] temp;
lock (this.Key)
temp = this.Handlers.ToArray();
return temp;
}
}
private List<DelegateBinder> Binders;
private DelegateBinder GetOrCreate(object key, bool shouldUseDefault = false)
{
lock (this.Binders)
{
var index = this.Binders.FindIndex(x => x.Key.Equals(key));
if (index != -1)
return this.Binders[index];
else
{
if (shouldUseDefault)
return default;
var item = new DelegateBinder(key);
this.Binders.Add(item);
return item;
}
}
}
public EventHandlerList()
{
this.Binders = new List<DelegateBinder>();
}
public void Register(object key, Delegate fn)
{
this.GetOrCreate(key).Register(fn);
}
public void Unregister(object key, Delegate fn)
{
var binder = this.GetOrCreate(key, true);
if (binder is null)
return;
binder.Unregister(fn);
if (binder.Count() == 0)
this.Binders.Remove(binder);
binder = null;
}
public void Invoke(object key, params object[] args)
{
var exceptions = new List<Exception>();
foreach (var handler in this.GetOrCreate(key).GetHandlers())
{
try { handler.DynamicInvoke(args); }
catch (Exception ex) { exceptions.Add(ex); }
}
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment