From f57b3c044b84efc0276dc68642422f5086daeb88 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 6 Mar 2016 15:23:47 +0100 Subject: [PATCH] Added ability to unsubscribe from weak events. --- .../Plugins/InpcPropertyAccessorPlugin.cs | 8 +- src/Perspex.Base/Utilities/IWeakSubscriber.cs | 21 ++++ .../Utilities/WeakSubscriptionManager.cs | 116 +++++++++++++----- 3 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 src/Perspex.Base/Utilities/IWeakSubscriber.cs diff --git a/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs b/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs index a35edc2572..4d53fdaea4 100644 --- a/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs @@ -99,10 +99,10 @@ namespace Perspex.Markup.Data.Plugins if (inpc != null) { - //WeakSubscriptionManager.Unsubscribe( - // inpc, - // nameof(inpc.PropertyChanged), - // this); + WeakSubscriptionManager.Unsubscribe( + inpc, + nameof(inpc.PropertyChanged), + this); } } diff --git a/src/Perspex.Base/Utilities/IWeakSubscriber.cs b/src/Perspex.Base/Utilities/IWeakSubscriber.cs new file mode 100644 index 0000000000..454c43c17f --- /dev/null +++ b/src/Perspex.Base/Utilities/IWeakSubscriber.cs @@ -0,0 +1,21 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Perspex.Utilities +{ + /// + /// Defines a listener to a event subscribed vis the . + /// + /// The type of the event arguments. + public interface IWeakSubscriber where T : EventArgs + { + /// + /// Invoked when the subscribed event is raised. + /// + /// The event sender. + /// The event arguments. + void OnEvent(object sender, T e); + } +} diff --git a/src/Perspex.Base/Utilities/WeakSubscriptionManager.cs b/src/Perspex.Base/Utilities/WeakSubscriptionManager.cs index 9adb9ff3ca..d307aad004 100644 --- a/src/Perspex.Base/Utilities/WeakSubscriptionManager.cs +++ b/src/Perspex.Base/Utilities/WeakSubscriptionManager.cs @@ -1,41 +1,89 @@ -using System; +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; namespace Perspex.Utilities { + /// + /// Manages subscriptions to events using weak listeners. + /// public static class WeakSubscriptionManager { - static class SubscriptionTypeStorage + /// + /// Subscribes to an event on an object using a weak subscription. + /// + /// The type of the event arguments. + /// The event source. + /// The name of the event. + /// The subscriber. + public static void Subscribe(object target, string eventName, IWeakSubscriber subscriber) + where T : EventArgs { - public static readonly ConditionalWeakTable> Subscribers - = new ConditionalWeakTable>(); + var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); + Subscription sub; + + if (!dic.TryGetValue(eventName, out sub)) + { + dic[eventName] = sub = new Subscription(dic, target, eventName); + + + } + sub.Add(new WeakReference>(subscriber)); } - class SubscriptionDic : Dictionary> + /// + /// Unsubscribes from an event. + /// + /// The type of the event arguments. + /// The event source. + /// The name of the event. + /// The subscriber. + public static void Unsubscribe(object target, string eventName, IWeakSubscriber subscriber) + where T : EventArgs { + SubscriptionDic dic; + if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out dic)) + { + Subscription sub; + + if (dic.TryGetValue(eventName, out sub)) + { + sub.Remove(subscriber); + } + } } + private static class SubscriptionTypeStorage + where T : EventArgs + { + public static readonly ConditionalWeakTable> Subscribers + = new ConditionalWeakTable>(); + } + private class SubscriptionDic : Dictionary> + where T : EventArgs + { + } - static readonly Dictionary> Accessors + private static readonly Dictionary> Accessors = new Dictionary>(); - class Subscription + private class Subscription where T : EventArgs { - WeakReference>[] _data = new WeakReference>[16]; - int _count = 0; - - readonly EventInfo _info; - readonly SubscriptionDic _sdic; + private readonly EventInfo _info; + private readonly SubscriptionDic _sdic; private readonly object _target; private readonly string _eventName; private readonly Delegate _delegate; + + private WeakReference>[] _data = new WeakReference>[16]; + private int _count = 0; + public Subscription(SubscriptionDic sdic, object target, string eventName) { _sdic = sdic; @@ -72,10 +120,32 @@ namespace Perspex.Utilities _count++; } + public void Remove(IWeakSubscriber s) + { + var removed = false; + + for (int c = 0; c < _count; ++c) + { + var reference = _data[c]; + IWeakSubscriber instance; + + if (reference != null && reference.TryGetTarget(out instance) && instance == s) + { + _data[c] = null; + removed = true; + } + } + + if (removed) + { + Compact(); + } + } + void Compact() { int empty = -1; - for (int c = 1; c < _count; c++) + for (int c = 0; c < _count; c++) { var r = _data[c]; //Mark current index as first empty @@ -111,19 +181,5 @@ namespace Perspex.Utilities Compact(); } } - - public static void Subscribe(object target, string eventName, IWeakSubscriber subscriber) - { - var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - Subscription sub; - if (!dic.TryGetValue(eventName, out sub)) - dic[eventName] = sub = new Subscription(dic, target, eventName); - sub.Add(new WeakReference>(subscriber)); - } - } - - public interface IWeakSubscriber - { - void OnEvent(object sender, T ev); } }