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