| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="WeakEventSource.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Reflection; |
| | | 10 | | |
| | | 11 | | namespace MyNet.Primitives.Events; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// Provides a weak event source implementation that allows subscribers to be garbage collected if they are no longer re |
| | | 15 | | /// </summary> |
| | | 16 | | /// <typeparam name="TEventArgs">The type of event arguments.</typeparam> |
| | | 17 | | public sealed class WeakEventSource<TEventArgs> |
| | | 18 | | where TEventArgs : EventArgs |
| | | 19 | | { |
| | 12 | 20 | | private readonly List<WeakSubscription> _subscriptions = []; |
| | | 21 | | |
| | | 22 | | /// <summary> |
| | | 23 | | /// Subscribes to the event with a weak reference to the handler, allowing the subscriber to be garbage collected if |
| | | 24 | | /// </summary> |
| | | 25 | | /// <param name="handler">The event handler to subscribe.</param> |
| | 12 | 26 | | public void Subscribe(EventHandler<TEventArgs> handler) => _subscriptions.Add(new(handler)); |
| | | 27 | | |
| | | 28 | | /// <summary> |
| | | 29 | | /// Unsubscribes from the event. |
| | | 30 | | /// </summary> |
| | | 31 | | /// <param name="handler">The event handler to unsubscribe.</param> |
| | 3 | 32 | | public void Unsubscribe(EventHandler<TEventArgs> handler) => _subscriptions.RemoveAll(x => x.Matches(handler)); |
| | | 33 | | |
| | | 34 | | /// <summary> |
| | | 35 | | /// Raises the event, invoking all subscribed handlers. |
| | | 36 | | /// </summary> |
| | | 37 | | /// <param name="sender">The source of the event.</param> |
| | | 38 | | /// <param name="args">The event arguments.</param> |
| | | 39 | | public void Raise(object sender, TEventArgs args) |
| | | 40 | | { |
| | 42 | 41 | | for (var i = _subscriptions.Count - 1; i >= 0; i--) |
| | | 42 | | { |
| | 9 | 43 | | if (!_subscriptions[i].TryInvoke(sender, args)) |
| | 3 | 44 | | _subscriptions.RemoveAt(i); |
| | | 45 | | } |
| | 12 | 46 | | } |
| | | 47 | | |
| | | 48 | | /// <summary> |
| | | 49 | | /// Represents a weak subscription to an event handler, holding a weak reference to the target object and the method |
| | | 50 | | /// </summary> |
| | | 51 | | private sealed class WeakSubscription |
| | | 52 | | { |
| | | 53 | | private readonly WeakReference? _targetReference; |
| | | 54 | | private readonly MethodInfo _method; |
| | | 55 | | |
| | | 56 | | /// <summary> |
| | | 57 | | /// Initializes a new instance of the <see cref="WeakSubscription"/> class with the specified event handler, sto |
| | | 58 | | /// </summary> |
| | | 59 | | /// <param name="handler">The event handler to subscribe.</param> |
| | | 60 | | public WeakSubscription(EventHandler<TEventArgs> handler) |
| | | 61 | | { |
| | 12 | 62 | | _method = handler.Method; |
| | | 63 | | |
| | 12 | 64 | | if (handler.Target != null) |
| | 9 | 65 | | _targetReference = new(handler.Target); |
| | 12 | 66 | | } |
| | | 67 | | |
| | | 68 | | /// <summary> |
| | | 69 | | /// Attempts to invoke the event handler with the specified sender and event arguments. If the target object has |
| | | 70 | | /// </summary> |
| | | 71 | | /// <param name="sender">The source of the event.</param> |
| | | 72 | | /// <param name="args">The event arguments.</param> |
| | | 73 | | /// <returns>True if the event handler was invoked; otherwise, false.</returns> |
| | | 74 | | public bool TryInvoke(object sender, TEventArgs args) |
| | | 75 | | { |
| | 9 | 76 | | if (_targetReference == null) |
| | | 77 | | { |
| | 3 | 78 | | _method.Invoke(null, [sender, args]); |
| | 3 | 79 | | return true; |
| | | 80 | | } |
| | | 81 | | |
| | 6 | 82 | | var target = _targetReference.Target; |
| | 6 | 83 | | if (target == null) |
| | 3 | 84 | | return false; |
| | | 85 | | |
| | 3 | 86 | | _method.Invoke(target, [sender, args]); |
| | 3 | 87 | | return true; |
| | | 88 | | } |
| | | 89 | | |
| | | 90 | | /// <summary> |
| | | 91 | | /// Determines whether the specified event handler matches the method and target of this weak subscription. This |
| | | 92 | | /// </summary> |
| | | 93 | | /// <param name="handler">The event handler to compare with this weak subscription.</param> |
| | | 94 | | /// <returns>True if the specified event handler matches the method and target of this weak subscription; otherw |
| | | 95 | | public bool Matches(EventHandler<TEventArgs> handler) => |
| | 3 | 96 | | _method == handler.Method && |
| | 3 | 97 | | _targetReference?.Target == handler.Target; |
| | | 98 | | } |
| | | 99 | | } |
| | | 100 | | |