Last active
April 1, 2020 23:27
-
-
Save nathan130200/0f7a6a8dbbae1083abe6a7d9e7fc8adf to your computer and use it in GitHub Desktop.
Thread-safe event handler system in C# using dynamic delegate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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