using System; using System.Collections.Generic; using System.Linq; using System.Reflection; //From http://caliburnmicro.codeplex.com/ namespace NzbDrone.Common.Eventing { /// /// Enables loosely-coupled publication of and subscription to events. /// public class EventAggregator : IEventAggregator { readonly List handlers = new List(); /// /// The default thread marshaller used for publication; /// public static Action DefaultPublicationThreadMarshaller = action => action(); /// /// Processing of handler results on publication thread. /// public static Action HandlerResultProcessing = (target, result) => { }; /// /// Initializes a new instance of the class. /// public EventAggregator() { PublicationThreadMarshaller = DefaultPublicationThreadMarshaller; } /// /// Gets or sets the default publication thread marshaller. /// /// /// The default publication thread marshaller. /// public Action PublicationThreadMarshaller { get; set; } /// /// Subscribes an instance to all events declared through implementations of /// /// The instance to subscribe for event publication. public virtual void Subscribe(object instance) { lock (handlers) { if (handlers.Any(x => x.Matches(instance))) { return; } handlers.Add(new Handler(instance)); } } /// /// Unsubscribes the instance from all events. /// /// The instance to unsubscribe. public virtual void Unsubscribe(object instance) { lock (handlers) { var found = handlers.FirstOrDefault(x => x.Matches(instance)); if (found != null) { handlers.Remove(found); } } } /// /// Publishes a message. /// /// The message instance. /// /// Does not marshall the the publication to any special thread by default. /// public virtual void Publish(object message) { Publish(message, PublicationThreadMarshaller); } /// /// Publishes a message. /// /// The message instance. /// Allows the publisher to provide a custom thread marshaller for the message publication. public virtual void Publish(object message, Action marshal) { Handler[] toNotify; lock (handlers) { toNotify = handlers.ToArray(); } marshal(() => { var messageType = message.GetType(); var dead = toNotify .Where(handler => !handler.Handle(messageType, message)) .ToList(); if (dead.Any()) { lock (handlers) { foreach (var item in dead) { handlers.Remove(item); } } } }); } class Handler { readonly WeakReference reference; readonly Dictionary supportedHandlers = new Dictionary(); public Handler(object handler) { reference = new WeakReference(handler); var interfaces = handler.GetType().GetInterfaces() .Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType); foreach (var @interface in interfaces) { var type = @interface.GetGenericArguments()[0]; var method = @interface.GetMethod("Handle"); supportedHandlers[type] = method; } } public bool Matches(object instance) { return reference.Target == instance; } public bool Handle(Type messageType, object message) { var target = reference.Target; if (target == null) { return false; } foreach (var pair in supportedHandlers) { if (pair.Key.IsAssignableFrom(messageType)) { var result = pair.Value.Invoke(target, new[] { message }); if (result != null) { HandlerResultProcessing(target, result); } return true; } } return true; } } } }