1
0

WeakEventHandlerManager.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.CompilerServices;
  8. namespace Avalonia.Utilities
  9. {
  10. /// <summary>
  11. /// Manages subscriptions to events using weak listeners.
  12. /// </summary>
  13. public static class WeakEventHandlerManager
  14. {
  15. /// <summary>
  16. /// Subscribes to an event on an object using a weak subscription.
  17. /// </summary>
  18. /// <typeparam name="TTarget">The type of the target.</typeparam>
  19. /// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
  20. /// <typeparam name="TSubscriber">The type of the subscriber.</typeparam>
  21. /// <param name="target">The event source.</param>
  22. /// <param name="eventName">The name of the event.</param>
  23. /// <param name="subscriber">The subscriber.</param>
  24. public static void Subscribe<TTarget, TEventArgs, TSubscriber>(TTarget target, string eventName, EventHandler<TEventArgs> subscriber)
  25. where TEventArgs : EventArgs where TSubscriber : class
  26. {
  27. var dic = SubscriptionTypeStorage<TEventArgs, TSubscriber>.Subscribers.GetOrCreateValue(target);
  28. Subscription<TEventArgs, TSubscriber> sub;
  29. if (!dic.TryGetValue(eventName, out sub))
  30. {
  31. dic[eventName] = sub = new Subscription<TEventArgs, TSubscriber>(dic, typeof(TTarget), target, eventName);
  32. }
  33. sub.Add(subscriber);
  34. }
  35. /// <summary>
  36. /// Unsubscribes from an event.
  37. /// </summary>
  38. /// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
  39. /// <typeparam name="TSubscriber">The type of the subscriber.</typeparam>
  40. /// <param name="target">The event source.</param>
  41. /// <param name="eventName">The name of the event.</param>
  42. /// <param name="subscriber">The subscriber.</param>
  43. public static void Unsubscribe<TEventArgs, TSubscriber>(object target, string eventName, EventHandler<TEventArgs> subscriber)
  44. where TEventArgs : EventArgs where TSubscriber : class
  45. {
  46. SubscriptionDic<TEventArgs, TSubscriber> dic;
  47. if (SubscriptionTypeStorage<TEventArgs, TSubscriber>.Subscribers.TryGetValue(target, out dic))
  48. {
  49. Subscription<TEventArgs, TSubscriber> sub;
  50. if (dic.TryGetValue(eventName, out sub))
  51. {
  52. sub.Remove(subscriber);
  53. }
  54. }
  55. }
  56. private static class SubscriptionTypeStorage<TArgs, TSubscriber>
  57. where TArgs : EventArgs where TSubscriber : class
  58. {
  59. public static readonly ConditionalWeakTable<object, SubscriptionDic<TArgs, TSubscriber>> Subscribers
  60. = new ConditionalWeakTable<object, SubscriptionDic<TArgs, TSubscriber>>();
  61. }
  62. private class SubscriptionDic<T, TSubscriber> : Dictionary<string, Subscription<T, TSubscriber>>
  63. where T : EventArgs where TSubscriber : class
  64. {
  65. }
  66. private static readonly Dictionary<Type, Dictionary<string, EventInfo>> Accessors
  67. = new Dictionary<Type, Dictionary<string, EventInfo>>();
  68. private class Subscription<T, TSubscriber> where T : EventArgs where TSubscriber : class
  69. {
  70. private readonly EventInfo _info;
  71. private readonly SubscriptionDic<T, TSubscriber> _sdic;
  72. private readonly object _target;
  73. private readonly string _eventName;
  74. private readonly Delegate _delegate;
  75. private Descriptor[] _data = new Descriptor[2];
  76. private int _count = 0;
  77. delegate void CallerDelegate(TSubscriber s, object sender, T args);
  78. struct Descriptor
  79. {
  80. public WeakReference<TSubscriber> Subscriber;
  81. public CallerDelegate Caller;
  82. }
  83. private static Dictionary<MethodInfo, CallerDelegate> s_Callers =
  84. new Dictionary<MethodInfo, CallerDelegate>();
  85. public Subscription(SubscriptionDic<T, TSubscriber> sdic, Type targetType, object target, string eventName)
  86. {
  87. _sdic = sdic;
  88. _target = target;
  89. _eventName = eventName;
  90. Dictionary<string, EventInfo> evDic;
  91. if (!Accessors.TryGetValue(targetType, out evDic))
  92. Accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
  93. if (!evDic.TryGetValue(eventName, out _info))
  94. {
  95. var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName);
  96. if (ev == null)
  97. {
  98. throw new ArgumentException(
  99. $"The event {eventName} was not found on {target.GetType()}.");
  100. }
  101. evDic[eventName] = _info = ev;
  102. }
  103. var del = new Action<object, T>(OnEvent);
  104. _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType, del.Target);
  105. _info.AddMethod.Invoke(target, new[] { _delegate });
  106. }
  107. void Destroy()
  108. {
  109. _info.RemoveMethod.Invoke(_target, new[] { _delegate });
  110. _sdic.Remove(_eventName);
  111. }
  112. public void Add(EventHandler<T> s)
  113. {
  114. Compact(true);
  115. if (_count == _data.Length)
  116. {
  117. //Extend capacity
  118. var ndata = new Descriptor[_data.Length*2];
  119. Array.Copy(_data, ndata, _data.Length);
  120. _data = ndata;
  121. }
  122. MethodInfo method = s.Method;
  123. var subscriber = (TSubscriber)s.Target;
  124. if (!s_Callers.TryGetValue(method, out var caller))
  125. s_Callers[method] = caller =
  126. (CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, method);
  127. _data[_count] = new Descriptor
  128. {
  129. Caller = caller,
  130. Subscriber = new WeakReference<TSubscriber>(subscriber)
  131. };
  132. _count++;
  133. }
  134. public void Remove(EventHandler<T> s)
  135. {
  136. var removed = false;
  137. for (int c = 0; c < _count; ++c)
  138. {
  139. var reference = _data[c].Subscriber;
  140. if (reference != null && reference.TryGetTarget(out TSubscriber instance) && Equals(instance, s.Target))
  141. {
  142. _data[c] = default;
  143. removed = true;
  144. }
  145. }
  146. if (removed)
  147. {
  148. Compact();
  149. }
  150. }
  151. void Compact(bool preventDestroy = false)
  152. {
  153. int empty = -1;
  154. for (int c = 0; c < _count; c++)
  155. {
  156. var r = _data[c];
  157. TSubscriber target = null;
  158. r.Subscriber?.TryGetTarget(out target);
  159. //Mark current index as first empty
  160. if (target == null && empty == -1)
  161. empty = c;
  162. //If current element isn't null and we have an empty one
  163. if (target != null && empty != -1)
  164. {
  165. _data[c] = default;
  166. _data[empty] = r;
  167. empty++;
  168. }
  169. }
  170. if (empty != -1)
  171. _count = empty;
  172. if (_count == 0 && !preventDestroy)
  173. Destroy();
  174. }
  175. void OnEvent(object sender, T eventArgs)
  176. {
  177. var needCompact = false;
  178. for(var c=0; c<_count; c++)
  179. {
  180. var r = _data[c].Subscriber;
  181. TSubscriber sub;
  182. if (r.TryGetTarget(out sub))
  183. {
  184. _data[c].Caller(sub, sender, eventArgs);
  185. }
  186. else
  187. needCompact = true;
  188. }
  189. if (needCompact)
  190. Compact();
  191. }
  192. }
  193. }
  194. }