# HG changeset patch # User Steven Robbins # Date 1268479283 0 # Branch messenger # Node ID d1a71995aa8149e71a4345b4b2105c17279ee2b9 # Parent fc8f518302951c5e8fa1eca21c2c58a8e591eee9 Removed requirement for destination - replaced with returned "token". Action is enough to keep reference alive (if neccessary) diff -r fc8f518302951c5e8fa1eca21c2c58a8e591eee9 -r d1a71995aa8149e71a4345b4b2105c17279ee2b9 TinyIoC.Tests/TinyMessengerTests.cs --- a/TinyIoC.Tests/TinyMessengerTests.cs Fri Mar 12 20:57:07 2010 +0000 +++ b/TinyIoC.Tests/TinyMessengerTests.cs Sat Mar 13 11:21:23 2010 +0000 @@ -8,6 +8,7 @@ namespace TinyIoC.Tests { + [TestClass] public class TinyMessengerTests { @@ -18,36 +19,37 @@ } [TestMethod] - public void Subscribe_ValidDestinationAndDeliverAction_DoesNotThrow() + public void Subscribe_ValidDeliverAction_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction)); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction)); } [TestMethod] - public void Subscribe_ValidDestinationAndDeliverActionWIthStrongReferences_DoesNotThrow() + public void SubScribe_ValidDeliveryAction_ReturnsRegistrationObject() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), true); + var output = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction)); + + Assert.IsInstanceOfType(output, typeof(TinyMessageSubscription)); } [TestMethod] - public void Subscribe_ValidDestinationDeliveryActionAndFilter_DoesNotThrow() + public void Subscribe_ValidDeliverActionWIthStrongReferences_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), true); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public void Subscribe_NullDestination_Throws() + public void Subscribe_ValidDeliveryActionAndFilter_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(null, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); } [TestMethod] @@ -56,7 +58,7 @@ { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, null, new Func(UtilityMethods.FakeMessageFilter)); + messenger.Subscribe(null, new Func(UtilityMethods.FakeMessageFilter)); } [TestMethod] @@ -65,39 +67,12 @@ { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), null); - } - - [TestMethod] - [ExpectedException(typeof(TinyMessengerSubscriptionException))] - public void Subscribe_SameDestinationAndEventTwice_ThrowsException() - { - var messenger = UtilityMethods.GetMessenger(); - - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); - } - - [TestMethod] - public void Unsubscribe_NoPreviousSubscription_DoesNotThrow() - { - var messenger = UtilityMethods.GetMessenger(); - - messenger.Unsubscribe(this); - } - - [TestMethod] - public void Unsubscribe_PreviousSubscription_DoesNotThrow() - { - var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); - - messenger.Unsubscribe(this); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), null); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] - public void Unsubscribe_NullDestination_Throws() + public void Unsubscribe_NullSubscriptionObject_Throws() { var messenger = UtilityMethods.GetMessenger(); @@ -105,13 +80,22 @@ } [TestMethod] - public void Unsubscribe_PreviousSubscription_CanSubscribeAgainWithoutThrowing() + public void Unsubscribe_PreviousSubscription_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); - messenger.Unsubscribe(this); + var subscription = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + messenger.Unsubscribe(subscription); + } + + [TestMethod] + public void Subscribe_PreviousSubscription_ReturnsDifferentSubscriptionObject() + { + var messenger = UtilityMethods.GetMessenger(); + var sub1 = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + var sub2 = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + + Assert.IsFalse(object.ReferenceEquals(sub1, sub2)); } [TestMethod] @@ -135,7 +119,7 @@ public void Publish_Subscriber_DoesNotThrow() { var messenger = UtilityMethods.GetMessenger(); - messenger.Subscribe(this, new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); messenger.Publish(new TestMessage(this)); } @@ -145,7 +129,7 @@ { var messenger = UtilityMethods.GetMessenger(); bool received = false; - messenger.Subscribe(this, (m) => { received = true; }); + messenger.Subscribe((m) => { received = true; }); messenger.Publish(new TestMessage(this)); @@ -153,11 +137,24 @@ } [TestMethod] + public void Publish_SubscribedThenUnsubscribedMessageNoFilter_DoesNotGetMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + var token = messenger.Subscribe((m) => { received = true; }); + messenger.Unsubscribe(token); + + messenger.Publish(new TestMessage(this)); + + Assert.IsFalse(received); + } + + [TestMethod] public void Publish_SubscribedMessageButFiltered_DoesNotGetMessage() { var messenger = UtilityMethods.GetMessenger(); bool received = false; - messenger.Subscribe(this, (m) => { received = true; }, (m) => false); + messenger.Subscribe((m) => { received = true; }, (m) => false); messenger.Publish(new TestMessage(this)); @@ -170,7 +167,7 @@ var messenger = UtilityMethods.GetMessenger(); ITinyMessage receivedMessage = null; var payload = new TestMessage(this); - messenger.Subscribe(this, (m) => { receivedMessage = m; }); + messenger.Subscribe((m) => { receivedMessage = m; }); messenger.Publish(payload); @@ -182,7 +179,7 @@ { var messenger = UtilityMethods.GetMessenger(); var output = string.Empty; - messenger.Subscribe>(this, (m) => { output = m._Content; }); + messenger.Subscribe>((m) => { output = m._Content; }); } [TestMethod] @@ -197,7 +194,7 @@ { var messenger = UtilityMethods.GetMessenger(); var output = string.Empty; - messenger.Subscribe>(this, (m) => { output = m._Content; }); + messenger.Subscribe>((m) => { output = m._Content; }); messenger.Publish(new GenericTinyMessage(this, "Testing")); Assert.AreEqual("Testing", output); diff -r fc8f518302951c5e8fa1eca21c2c58a8e591eee9 -r d1a71995aa8149e71a4345b4b2105c17279ee2b9 TinyIoC/TinyMessenger.cs --- a/TinyIoC/TinyMessenger.cs Fri Mar 12 20:57:07 2010 +0000 +++ b/TinyIoC/TinyMessenger.cs Sat Mar 13 11:21:23 2010 +0000 @@ -84,6 +84,13 @@ _Content = content; } } + + /// + /// Represents an active subscription to a message + /// + public sealed class TinyMessageSubscription + { + } #endregion #region Exceptions @@ -121,10 +128,9 @@ /// All messages of this type will be delivered. /// /// Type of message - /// 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 - void Subscribe(object destination, Action deliveryAction) where TMessage : class, ITinyMessage; + /// TinyMessageSubscription used to unsubscribing + TinyMessageSubscription Subscribe(Action deliveryAction) where TMessage : class, ITinyMessage; /// /// Subscribe to a message type with the given destination and delivery action. @@ -132,11 +138,10 @@ /// 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; + /// TinyMessageSubscription used to unsubscribing + TinyMessageSubscription Subscribe(Action deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage; /// /// Subscribe to a message type with the given destination and delivery action with the given filter. @@ -145,10 +150,9 @@ /// Only messages that "pass" the filter will be delivered. /// /// Type of message - /// 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 - void Subscribe(object destination, Action deliveryAction, Func messageFilter) where TMessage : class, ITinyMessage; + /// TinyMessageSubscription used to unsubscribing + TinyMessageSubscription Subscribe(Action deliveryAction, Func messageFilter) where TMessage : class, ITinyMessage; /// /// Subscribe to a message type with the given destination and delivery action with the given filter. @@ -157,11 +161,10 @@ /// 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; + /// TinyMessageSubscription used to unsubscribing + TinyMessageSubscription Subscribe(Action deliveryAction, Func messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage; /// /// Unsubscribe from a particular message type. @@ -170,7 +173,7 @@ /// /// Type of message /// Destination (usually "this") that was used to subscribe initially - void Unsubscribe(object destination) where TMessage : class, ITinyMessage; + void Unsubscribe(TinyMessageSubscription subscription) where TMessage : class, ITinyMessage; /// /// Publish a message to any subscribers @@ -192,38 +195,35 @@ // can't think of any other way without generic contravariance? private interface ITinyMessageSubscription { - object Destination { get; } - bool CanDeliver(ITinyMessage message); + TinyMessageSubscription Subscription { get; } + bool ShouldAttemptDelivery(ITinyMessage message); void Deliver(ITinyMessage message); } private class WeakTinyMessageSubscription : ITinyMessageSubscription where TMessage : class, ITinyMessage { - protected WeakReference _Destination; + protected TinyMessageSubscription _Subscription; protected WeakReference _DeliveryAction; protected WeakReference _MessageFilter; - public object Destination + public TinyMessageSubscription Subscription { - get { return _Destination.Target; } + get { return _Subscription; } } - public bool CanDeliver(ITinyMessage message) + public bool ShouldAttemptDelivery(ITinyMessage message) { if (!(message is TMessage)) return false; - if (!_Destination.IsAlive) - return false; - if (!_DeliveryAction.IsAlive) return false; if (!_MessageFilter.IsAlive) return false; - return ((Func) _MessageFilter.Target).Invoke(message as TMessage); + return ((Func)_MessageFilter.Target).Invoke(message as TMessage); } public void Deliver(ITinyMessage message) @@ -231,15 +231,12 @@ if (!(message is TMessage)) throw new ArgumentException("Message is not the correct type"); - if (!_Destination.IsAlive) - return; - if (!_DeliveryAction.IsAlive) return; try { - ((Action) _DeliveryAction.Target).Invoke(message as TMessage); + ((Action)_DeliveryAction.Target).Invoke(message as TMessage); } catch (Exception) { @@ -253,10 +250,10 @@ /// Destination object /// Delivery action /// Filter function - public WeakTinyMessageSubscription(object destination, Action deliveryAction, Func messageFilter) + public WeakTinyMessageSubscription(TinyMessageSubscription subscription, Action deliveryAction, Func messageFilter) { - if (destination == null) - throw new ArgumentNullException("destination"); + if (subscription == null) + throw new ArgumentNullException("subscription"); if (deliveryAction == null) throw new ArgumentNullException("deliveryAction"); @@ -264,7 +261,7 @@ if (messageFilter == null) throw new ArgumentNullException("messageFilter"); - _Destination = new WeakReference(destination); + _Subscription = subscription; _DeliveryAction = new WeakReference(deliveryAction); _MessageFilter = new WeakReference(messageFilter); } @@ -273,16 +270,16 @@ private class StrongTinyMessageSubscription : ITinyMessageSubscription where TMessage : class, ITinyMessage { - protected object _Destination; + protected TinyMessageSubscription _Subscription; protected Action _DeliveryAction; protected Func _MessageFilter; - public object Destination + public TinyMessageSubscription Subscription { - get { return _Destination; } + get { return _Subscription; } } - public bool CanDeliver(ITinyMessage message) + public bool ShouldAttemptDelivery(ITinyMessage message) { if (!(message is TMessage)) return false; @@ -311,10 +308,10 @@ /// Destination object /// Delivery action /// Filter function - public StrongTinyMessageSubscription(object destination, Action deliveryAction, Func messageFilter) + public StrongTinyMessageSubscription(TinyMessageSubscription subscription, Action deliveryAction, Func messageFilter) { - if (destination == null) - throw new ArgumentNullException("destination"); + if (subscription == null) + throw new ArgumentNullException("subscription"); if (deliveryAction == null) throw new ArgumentNullException("deliveryAction"); @@ -322,7 +319,7 @@ if (messageFilter == null) throw new ArgumentNullException("messageFilter"); - _Destination = destination; + _Subscription = subscription; _DeliveryAction = deliveryAction; _MessageFilter = messageFilter; } @@ -342,12 +339,11 @@ /// All messages of this type will be delivered. /// /// Type of message - /// 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 + /// TinyMessageSubscription used to unsubscribing + public TinyMessageSubscription Subscribe(Action deliveryAction) where TMessage : class, ITinyMessage { - AddSubscriptionInternal(destination, deliveryAction, (m) => true, false); + return AddSubscriptionInternal(deliveryAction, (m) => true, false); } /// @@ -356,13 +352,12 @@ /// 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 + /// TinyMessageSubscription used to unsubscribing + public TinyMessageSubscription Subscribe(Action deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage { - AddSubscriptionInternal(destination, deliveryAction, (m) => true, useStrongReferences); + return AddSubscriptionInternal(deliveryAction, (m) => true, useStrongReferences); } /// @@ -372,12 +367,11 @@ /// Only messages that "pass" the filter will be delivered. /// /// Type of message - /// 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 + /// TinyMessageSubscription used to unsubscribing + public TinyMessageSubscription Subscribe(Action deliveryAction, Func messageFilter) where TMessage : class, ITinyMessage { - AddSubscriptionInternal(destination, deliveryAction, messageFilter, false); + return AddSubscriptionInternal(deliveryAction, messageFilter, false); } /// @@ -387,13 +381,12 @@ /// 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 + /// TinyMessageSubscription used to unsubscribing + public TinyMessageSubscription Subscribe(Action deliveryAction, Func messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage { - AddSubscriptionInternal(destination, deliveryAction, messageFilter, useStrongReferences); + return AddSubscriptionInternal(deliveryAction, messageFilter, useStrongReferences); } /// @@ -403,9 +396,9 @@ /// /// Type of message /// Destination (usually "this") that was used to subscribe initially - public void Unsubscribe(object destination) where TMessage : class, ITinyMessage + public void Unsubscribe(TinyMessageSubscription subscription) where TMessage : class, ITinyMessage { - RemoveSubscriptionInternal(destination); + RemoveSubscriptionInternal(subscription); } /// @@ -420,12 +413,9 @@ #endregion #region Internal Methods - private void AddSubscriptionInternal(object destination, Action deliveryAction, Func messageFilter, bool strongReference) + private TinyMessageSubscription AddSubscriptionInternal(Action deliveryAction, Func messageFilter, bool strongReference) where TMessage : class, ITinyMessage { - if (destination == null) - throw new ArgumentNullException("destination"); - if (deliveryAction == null) throw new ArgumentNullException("deliveryAction"); @@ -442,25 +432,22 @@ _Subscriptions[typeof(TMessage)] = currentSubscriptions; } - var currentlySubscribed = from sub in currentSubscriptions - where (sub.Destination != null) && (object.ReferenceEquals(sub.Destination, destination)) - select sub; - - if (currentlySubscribed.Count() != 0) - throw new TinyMessengerSubscriptionException(typeof(TMessage), "An existing subscription for that message type and destination already exists"); + var subscription = new TinyMessageSubscription(); if (strongReference) - currentSubscriptions.Add(new StrongTinyMessageSubscription(destination, deliveryAction, messageFilter)); + currentSubscriptions.Add(new StrongTinyMessageSubscription(subscription, deliveryAction, messageFilter)); else - currentSubscriptions.Add(new WeakTinyMessageSubscription(destination, deliveryAction, messageFilter)); + currentSubscriptions.Add(new WeakTinyMessageSubscription(subscription, deliveryAction, messageFilter)); + + return subscription; } } - private void RemoveSubscriptionInternal(object destination) + private void RemoveSubscriptionInternal(TinyMessageSubscription subscription) where TMessage : class, ITinyMessage { - if (destination == null) - throw new ArgumentNullException("destination"); + if (subscription == null) + throw new ArgumentNullException("subscription"); lock (_SubscriptionsPadlock) { @@ -469,7 +456,7 @@ return; var currentlySubscribed = (from sub in currentSubscriptions - where (sub.Destination != null) && (object.ReferenceEquals(sub.Destination, destination)) + where object.ReferenceEquals(sub.Subscription, subscription) select sub).ToList(); currentlySubscribed.ForEach(sub => currentSubscriptions.Remove(sub)); @@ -490,7 +477,7 @@ return; currentlySubscribed = (from sub in currentSubscriptions - where (sub.Destination != null) && (sub.CanDeliver(message)) + where sub.ShouldAttemptDelivery(message) select sub).ToList(); }