See https://dotnetfiddle.net/zv5aNg to run the sample program.
For context, please see: https://codereview.stackexchange.com/a/221525/75659
See https://dotnetfiddle.net/zv5aNg to run the sample program.
For context, please see: https://codereview.stackexchange.com/a/221525/75659
using System; | |
using System.Collections.Generic; | |
public struct EventHandlerProxy<T> | |
{ | |
private struct HandlerProxy | |
{ | |
public Action<T> proxy; | |
public int count; | |
} | |
private Dictionary<object, HandlerProxy> handlerProxies; | |
public Action<T> Add(object handler) | |
{ | |
if(handler.GetType() == typeof(Action<T>)) | |
{ | |
return (Action<T>)handler; | |
} | |
if (handlerProxies == null) | |
{ | |
handlerProxies = new Dictionary<object, HandlerProxy>(); | |
} | |
if (handlerProxies.TryGetValue(handler, out HandlerProxy handlerProxy)) | |
{ | |
handlerProxy.count += 1; | |
handlerProxies[handler] = handlerProxy; | |
} | |
else | |
{ | |
handlerProxy = new HandlerProxy() { count = 1 }; | |
if (handler is Action<object>) | |
{ | |
handlerProxy.proxy = (v) => ((Action<object>)handler).Invoke(v); | |
} | |
else if (handler is Action) | |
{ | |
handlerProxy.proxy = (v) => ((Action)handler).Invoke(); | |
} | |
// Handles cases where handler is Action<S> where S inherits from T | |
// Q: Why not just return ((Action<T>)handler? | |
// A: While the cast is valid and even invokable, it can't be added to an Action<T> invocation list. | |
else | |
{ | |
handlerProxy.proxy = (v) => ((Action<T>)handler).Invoke(v); | |
} | |
handlerProxies.Add(handler, handlerProxy); | |
} | |
return handlerProxy.proxy; | |
} | |
public Action<T> Remove(object handler) | |
{ | |
if (handler.GetType() == typeof(Action<T>)) | |
{ | |
return (Action<T>)handler; | |
} | |
if (handlerProxies == null) | |
{ | |
handlerProxies = new Dictionary<object, HandlerProxy>(); | |
} | |
HandlerProxy entry; | |
if (handlerProxies.TryGetValue(handler, out entry)) | |
{ | |
entry.count -= 1; | |
if (entry.count == 0) | |
{ | |
handlerProxies.Remove(handler); | |
} | |
else | |
{ | |
handlerProxies[handler] = entry; | |
} | |
} | |
return entry.proxy; | |
} | |
} |
using System; | |
public class Foo<T> : IFoo<T> | |
{ | |
private EventHandlerProxy<T> changeProxy; | |
public event Action<T> ChangeValue = delegate {}; | |
public event Action<object> ChangeObject | |
{ | |
add => ChangeValue += changeProxy.Add(value); | |
remove => ChangeValue -= changeProxy.Remove(value); | |
} | |
public event Action ChangeEmpty | |
{ | |
add => ChangeValue += changeProxy.Add(value); | |
remove => ChangeValue -= changeProxy.Remove(value); | |
} | |
public void InvokeChange(T value) | |
{ | |
ChangeValue.Invoke(value); | |
} | |
} |
using System; | |
public interface IFoo<T> | |
{ | |
event Action<T> ChangeValue; | |
event Action<object> ChangeObject; | |
event Action ChangeEmpty; | |
void InvokeChange(T value); | |
} |
using System; | |
public class Program | |
{ | |
public static void Main() | |
{ | |
IFoo<int> test1 = new Foo<int>(); | |
test1.ChangeEmpty += () => Console.WriteLine("0) Empty!"); | |
test1.ChangeObject += (o) => Console.WriteLine("1) Object: {0}", o); | |
test1.ChangeValue += (v) => Console.WriteLine("2) Value: {0}", v); | |
test1.ChangeObject += (o) => Console.WriteLine("3) Object: {0}", o); | |
test1.ChangeEmpty += () => Console.WriteLine("4) Empty!"); | |
test1.InvokeChange(123); | |
Console.WriteLine("\n--------\n"); | |
IFoo<int> test2 = new Foo<int>(); | |
test2.ChangeEmpty += EmptyHandler; | |
test2.ChangeObject += ObjectHandler; | |
Console.WriteLine("1) EMPTY, OBJECT"); | |
test2.InvokeChange(1); | |
test2.ChangeEmpty -= EmptyHandler; | |
test2.ChangeValue += ValueHandler; | |
Console.WriteLine("2) OBJECT, VALUE"); | |
test2.InvokeChange(2); | |
test2.ChangeObject -= ObjectHandler; | |
Console.WriteLine("3) VALUE "); | |
test2.InvokeChange(3); | |
test2.ChangeObject += ObjectHandler; | |
test2.ChangeEmpty += EmptyHandler; | |
test2.ChangeValue += ValueHandler; | |
Console.WriteLine("4) VALUE, OBJECT, EMPTY, VALUE"); | |
test2.InvokeChange(4); | |
test2.ChangeValue -= ValueHandler; | |
test2.ChangeValue -= ValueHandler; | |
test2.ChangeEmpty -= EmptyHandler; | |
test2.ChangeObject -= ObjectHandler; | |
Console.WriteLine("5) <NONE>"); | |
test2.InvokeChange(5); | |
} | |
static void EmptyHandler() { Console.WriteLine(" - Empty!"); } | |
static void ObjectHandler(object val) { Console.WriteLine(" - Object: {0}", val); } | |
static void ValueHandler(int val) { Console.WriteLine(" - Value: {0}", val); } | |
} |