# HG changeset patch # User Steven Robbins # Date 1268426148 0 # Branch messenger # Node ID dfbc1653c7aa31633a27be8810672bf4905e7e8b # Parent 4b22d23e7e3a47cc32cb6126e5149debf0157e95 Added strong reference support diff -r 4b22d23e7e3a47cc32cb6126e5149debf0157e95 -r dfbc1653c7aa31633a27be8810672bf4905e7e8b TinyIoC.Tests/TinyMessengerTests.cs --- a/TinyIoC.Tests/TinyMessengerTests.cs Fri Mar 12 17:39:53 2010 +0000 +++ b/TinyIoC.Tests/TinyMessengerTests.cs Fri Mar 12 20:35:48 2010 +0000 @@ -26,6 +26,14 @@ } [TestMethod] + public void Subscribe_ValidDestinationAndDeliverActionWIthStrongReferences_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), true); + } + + [TestMethod] public void Subscribe_ValidDestinationDeliveryActionAndFilter_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); diff -r 4b22d23e7e3a47cc32cb6126e5149debf0157e95 -r dfbc1653c7aa31633a27be8810672bf4905e7e8b TinyIoC/TinyMessenger.cs --- a/TinyIoC/TinyMessenger.cs Fri Mar 12 17:39:53 2010 +0000 +++ b/TinyIoC/TinyMessenger.cs Fri Mar 12 20:35:48 2010 +0000 @@ -116,27 +116,54 @@ { /// /// Subscribe to a message type with the given destination and delivery action. + /// All references are held with WeakReferences /// /// All messages of this type will be delivered. /// /// Type of message - /// Destination (usually "this") - held in a weak reference and used for tracking if the recipient is GCd and for Unsubscribe + /// Destination (usually "this") - used for Unsubscribe /// Action to invoke when message is delivered - /// Thrown when attempting to subscribe more than once with the same destination and message type + /// Thrown when attempting to subscribe more than once with the same destination and message type void Subscribe(object destination, Action deliveryAction) where TMessage : class, ITinyMessage; /// + /// Subscribe to a message type with the given destination and delivery action. + /// + /// All messages of this type will be delivered. + /// + /// Type of message + /// Destination (usually "this") - used for Unsubscribe + /// Action to invoke when message is delivered + /// Use strong references to destination and deliveryAction + /// Thrown when attempting to subscribe more than once with the same destination and message type + void Subscribe(object destination, Action deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage; + + /// /// Subscribe to a message type with the given destination and delivery action with the given filter. + /// All references are held with WeakReferences /// /// Only messages that "pass" the filter will be delivered. /// /// Type of message - /// Destination (usually "this") - held in a weak reference and used for tracking if the recipient is GCd + /// Destination (usually "this") - used for Unsubscribe /// Action to invoke when message is delivered - /// Thrown when attempting to subscribe more than once with the same destination and message type + /// Thrown when attempting to subscribe more than once with the same destination and message type void Subscribe(object destination, Action deliveryAction, Func messageFilter) where TMessage : class, ITinyMessage; /// + /// Subscribe to a message type with the given destination and delivery action with the given filter. + /// All references are held with WeakReferences + /// + /// Only messages that "pass" the filter will be delivered. + /// + /// Type of message + /// Destination (usually "this") - held in a weak reference used for Unsubscribe + /// Action to invoke when message is delivered + /// Use strong references to destination and deliveryAction + /// Thrown when attempting to subscribe more than once with the same destination and message type + void Subscribe(object destination, Action deliveryAction, Func messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage; + + /// /// Unsubscribe from a particular message type. /// /// Does not throw an exception if the subscription is not found. @@ -170,12 +197,12 @@ void Deliver(ITinyMessage message); } - private class TinyMessageSubscription : ITinyMessageSubscription + private class WeakTinyMessageSubscription : ITinyMessageSubscription where TMessage : class, ITinyMessage { protected WeakReference _Destination; - protected Action _DeliveryAction; - protected Func _MessageFilter; + protected WeakReference _DeliveryAction; + protected WeakReference _MessageFilter; public object Destination { @@ -190,7 +217,13 @@ if (!_Destination.IsAlive) return false; - return _MessageFilter.Invoke(message as TMessage); + if (!_DeliveryAction.IsAlive) + return false; + + if (!_MessageFilter.IsAlive) + return false; + + return ((Func) _MessageFilter.Target).Invoke(message as TMessage); } public void Deliver(ITinyMessage message) @@ -201,6 +234,67 @@ if (!_Destination.IsAlive) return; + if (!_DeliveryAction.IsAlive) + return; + + try + { + ((Action) _DeliveryAction.Target).Invoke(message as TMessage); + } + catch (Exception) + { + // We don't want publish exceptions to bubble up + } + } + + /// + /// Initializes a new instance of the WeakTinyMessageSubscription class. + /// + /// Destination object + /// Delivery action + /// Filter function + public WeakTinyMessageSubscription(object destination, Action deliveryAction, Func messageFilter) + { + if (destination == null) + throw new ArgumentNullException("destination"); + + if (deliveryAction == null) + throw new ArgumentNullException("deliveryAction"); + + if (messageFilter == null) + throw new ArgumentNullException("messageFilter"); + + _Destination = new WeakReference(destination); + _DeliveryAction = new WeakReference(deliveryAction); + _MessageFilter = new WeakReference(messageFilter); + } + } + + private class StrongTinyMessageSubscription : ITinyMessageSubscription + where TMessage : class, ITinyMessage + { + protected object _Destination; + protected Action _DeliveryAction; + protected Func _MessageFilter; + + public object Destination + { + get { return _Destination; } + } + + public bool CanDeliver(ITinyMessage message) + { + if (!(message is TMessage)) + return false; + + return _MessageFilter.Invoke(message as TMessage); + } + + public void Deliver(ITinyMessage message) + { + if (!(message is TMessage)) + throw new ArgumentException("Message is not the correct type"); + try { _DeliveryAction.Invoke(message as TMessage); @@ -217,7 +311,7 @@ /// Destination object /// Delivery action /// Filter function - public TinyMessageSubscription(object destination, Action deliveryAction, Func messageFilter) + public StrongTinyMessageSubscription(object destination, Action deliveryAction, Func messageFilter) { if (destination == null) throw new ArgumentNullException("destination"); @@ -228,7 +322,7 @@ if (messageFilter == null) throw new ArgumentNullException("messageFilter"); - _Destination = new WeakReference(destination); + _Destination = destination; _DeliveryAction = deliveryAction; _MessageFilter = messageFilter; } @@ -243,30 +337,63 @@ #region Public API /// /// Subscribe to a message type with the given destination and delivery action. + /// All references are held with WeakReferences /// /// All messages of this type will be delivered. /// /// Type of message - /// Destination (usually "this") - held in a weak reference and used for tracking if the recipient is GCd and Unsubscribe + /// Destination (usually "this") - used for Unsubscribe /// Action to invoke when message is delivered /// Thrown when attempting to subscribe more than once with the same destination and message type public void Subscribe(object destination, Action deliveryAction) where TMessage : class, ITinyMessage { - Subscribe(destination, deliveryAction, (m) => true); + AddSubscriptionInternal(destination, deliveryAction, (m) => true, false); + } + + /// + /// Subscribe to a message type with the given destination and delivery action. + /// + /// All messages of this type will be delivered. + /// + /// Type of message + /// Destination (usually "this") - used for Unsubscribe + /// Action to invoke when message is delivered + /// Use strong references to destination and deliveryAction + /// Thrown when attempting to subscribe more than once with the same destination and message type + public void Subscribe(object destination, Action deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage + { + AddSubscriptionInternal(destination, deliveryAction, (m) => true, useStrongReferences); } /// /// Subscribe to a message type with the given destination and delivery action with the given filter. + /// All references are held with WeakReferences /// /// Only messages that "pass" the filter will be delivered. /// /// Type of message - /// Destination (usually "this") - held in a weak reference and used for tracking if the recipient is GCd + /// Destination (usually "this") - used for Unsubscribe /// Action to invoke when message is delivered /// Thrown when attempting to subscribe more than once with the same destination and message type public void Subscribe(object destination, Action deliveryAction, Func messageFilter) where TMessage : class, ITinyMessage { - AddSubscriptionInternal(destination, deliveryAction, messageFilter); + AddSubscriptionInternal(destination, deliveryAction, messageFilter, false); + } + + /// + /// Subscribe to a message type with the given destination and delivery action with the given filter. + /// All references are held with WeakReferences + /// + /// Only messages that "pass" the filter will be delivered. + /// + /// Type of message + /// Destination (usually "this") - held in a weak reference used for Unsubscribe + /// Action to invoke when message is delivered + /// Use strong references to destination and deliveryAction + /// Thrown when attempting to subscribe more than once with the same destination and message type + public void Subscribe(object destination, Action deliveryAction, Func messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage + { + AddSubscriptionInternal(destination, deliveryAction, messageFilter, useStrongReferences); } /// @@ -293,7 +420,7 @@ #endregion #region Internal Methods - private void AddSubscriptionInternal(object destination, Action deliveryAction, Func messageFilter) + private void AddSubscriptionInternal(object destination, Action deliveryAction, Func messageFilter, bool strongReference) where TMessage : class, ITinyMessage { if (destination == null) @@ -322,7 +449,10 @@ if (currentlySubscribed.Count() != 0) throw new TinyMessengerSubscriptionException(typeof(TMessage), "An existing subscription for that message type and destination already exists"); - currentSubscriptions.Add(new TinyMessageSubscription(destination, deliveryAction, messageFilter)); + if (strongReference) + currentSubscriptions.Add(new StrongTinyMessageSubscription(destination, deliveryAction, messageFilter)); + else + currentSubscriptions.Add(new WeakTinyMessageSubscription(destination, deliveryAction, messageFilter)); } }