Create your public event, in its implementations add and remove, sign the subscriber directly with a private object event:
using System; using System.Timers; class Program { static void Main() { MyTimer myTimer = new MyTimer(); myTimer.Tick += (o, e) => Console.WriteLine("Hello!"); Console.ReadLine(); } } class MyTimer { Timer timer = new Timer(1000); public MyTimer() { timer.Start(); } public event ElapsedEventHandler Tick { add => timer.Elapsed += value; remove => timer.Elapsed -= value; } }
If you strongly want to - you can expand the idea to something like this, now the private object will not “leak”, and the event signature can be changed:
class MyTimer { Timer timer = new Timer(333); public MyTimer() { timer.Start(); } Dictionary<ElapsedEventHandler, Action> handlers = new Dictionary<ElapsedEventHandler, Action>(); public event Action Tick { add { if (value == null) return; ElapsedEventHandler h = (o, e) => value.Invoke(); handlers[h] = value; timer.Elapsed += h; } remove { if (value == null) return; ElapsedEventHandler h = handlers.FirstOrDefault( pair => pair.Value == value).Key; if (h == null) return; timer.Elapsed -= h; handlers.Remove(h); } } }
Please note that the key and the value in the dictionary cannot be changed in places; otherwise, if you re-subscribe the same method, you will not be able to unsubscribe from the created lambda they will be the same)
You can test this code:
class Program { static void Main() { MyTimer myTimer = new MyTimer(); myTimer.Tick += MyTimer_Tick1; Console.ReadLine(); myTimer.Tick += MyTimer_Tick2; Console.ReadLine(); myTimer.Tick += MyTimer_Tick2; Console.ReadLine(); myTimer.Tick -= MyTimer_Tick2; Console.ReadLine(); myTimer.Tick -= MyTimer_Tick2; Console.ReadLine(); myTimer.Tick -= MyTimer_Tick2; Console.ReadLine(); } private static void MyTimer_Tick2() => Console.WriteLine(2); private static void MyTimer_Tick1() => Console.WriteLine(1); }
Repeated subscriptions and unsubscriptions work correctly; also unsubscribing an unsigned method does not throw exceptions.
If you pervert a little more, it may even be possible to figure out a generic class for converting different delegates, which encapsulates the dictionary and all the dirty work, but, for me, this is already a dash
I took my own interest, sketched a class, but for some reason the answer is not working, can someone in the comments tell me how to fix it ...
class DelegateConverter<TIn, TOut> where TIn : class where TOut : class { Dictionary<TOut, TIn> handlers = new Dictionary<TOut, TIn>(); Func<TIn, TOut> _convertFunc; public DelegateConverter(Func<TIn, TOut> convertFunc) { _convertFunc = convertFunc; } public TOut Add(TIn d) { if (d == null) return null; TOut h = _convertFunc(d); handlers[h] = d; return h; } public TOut Remove(TIn d) { if (d == null) return null; TOut h = null; foreach (var pair in handlers) if (pair.Value == d) h = pair.Key; //TOut h = handlers.FirstOrDefault(pair => pair.Value == d).Key; if (h == null) return null; handlers.Remove(h); return h; } }
Here in the Remove method, for some reason, always h == null ...
The rewritten method of our MyTimer :
class MyTimer { Timer timer = new Timer(333); public MyTimer() { timer.Start(); } DelegateConverter<Action, ElapsedEventHandler> dConverter = new DelegateConverter<Action, ElapsedEventHandler>(v => (o, e) => v.Invoke()); public event Action Tick { add => timer.Elapsed += dConverter.Add(value); remove => timer.Elapsed -= dConverter.Remove(value); } }
We test the old