using System; using System.Collections.Generic; using System.Reflection; using static System.String; namespace Xamarin.Forms { internal class WeakEventManager { readonly Dictionary> _eventHandlers = new Dictionary>(); public void AddEventHandler(string eventName, EventHandler handler) where TEventArgs : EventArgs { if (IsNullOrEmpty(eventName)) { throw new ArgumentNullException(nameof(eventName)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } AddEventHandler(eventName, handler.Target, handler.GetMethodInfo()); } public void AddEventHandler(string eventName, EventHandler handler) { if (IsNullOrEmpty(eventName)) { throw new ArgumentNullException(nameof(eventName)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } AddEventHandler(eventName, handler.Target, handler.GetMethodInfo()); } public void HandleEvent(object sender, object args, string eventName) { var toRaise = new List>(); var toRemove = new List(); List target; if (_eventHandlers.TryGetValue(eventName, out target)) { foreach (Subscription subscription in target) { bool isStatic = subscription.Subscriber == null; if (isStatic) { // For a static method, we'll just pass null as the first parameter of MethodInfo.Invoke toRaise.Add(Tuple.Create(null, subscription.Handler)); continue; } object subscriber = subscription.Subscriber.Target; if (subscriber == null) { // The subscriber was collected, so there's no need to keep this subscription around toRemove.Add(subscription); } else { toRaise.Add(Tuple.Create(subscriber, subscription.Handler)); } } foreach (Subscription subscription in toRemove) { target.Remove(subscription); } } foreach (Tuple tuple in toRaise) { tuple.Item2.Invoke(tuple.Item1, new[] { sender, args }); } } public void RemoveEventHandler(string eventName, EventHandler handler) where TEventArgs : EventArgs { if (IsNullOrEmpty(eventName)) { throw new ArgumentNullException(nameof(eventName)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } RemoveEventHandler(eventName, handler.Target, handler.GetMethodInfo()); } public void RemoveEventHandler(string eventName, EventHandler handler) { if (IsNullOrEmpty(eventName)) { throw new ArgumentNullException(nameof(eventName)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } RemoveEventHandler(eventName, handler.Target, handler.GetMethodInfo()); } void AddEventHandler(string eventName, object handlerTarget, MethodInfo methodInfo) { List targets; if (!_eventHandlers.TryGetValue(eventName, out targets)) { targets = new List(); _eventHandlers.Add(eventName, targets); } if (handlerTarget == null) { // This event handler is a static method targets.Add(new Subscription(null, methodInfo)); return; } targets.Add(new Subscription(new WeakReference(handlerTarget), methodInfo)); } void RemoveEventHandler(string eventName, object handlerTarget, MemberInfo methodInfo) { List subscriptions; if (!_eventHandlers.TryGetValue(eventName, out subscriptions)) { return; } for (int n = subscriptions.Count; n > 0; n--) { Subscription current = subscriptions[n - 1]; if (current.Subscriber != handlerTarget || current.Handler.Name != methodInfo.Name) { continue; } subscriptions.Remove(current); } } struct Subscription { public Subscription(WeakReference subscriber, MethodInfo handler) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } Subscriber = subscriber; Handler = handler; } public readonly WeakReference Subscriber; public readonly MethodInfo Handler; } } }