Click here to Skip to main content
15,923,789 members
Articles / Programming Languages / C# 5.0

Publisher/Subscriber pattern with Event/Delegate and EventAggregator

Rate me:
Please Sign up or sign in to vote.
4.73/5 (31 votes)
12 Feb 2015CPOL6 min read 155.7K   2.8K   85   46
Publisher/Subscriber pattern with Event/Delegate and EventAggregator

Publisher/Subscriber pattern

Publisher/Subscriber pattern is one of the variations of the Observer designer pattern introduced by GOF in software development. In Publisher/Subscriber pattern publisher (entry responsible for publishing message) publish message and there are one or more Subscriber (entity who subscribe (i.e. interested in message) to particular message type)  who capture published message. Below image describe the scenario of publisher and subscriber pattern where Publisher publish two type of message (MessageA and MessageB) and Subscribers of the message receive the message in which they subscribed (Subscriber1 capture MessageA and Subscriber2 & Subscriber3 capture MessageB).

  

To understand this consider Real life senario where Mobile operators sending message to their customers.

As in above image Mobile operator publisher(broadcast) message (Message of Cricket Score and Message of Latest News) and message capture by the customer cells who subscribed for the message(Customer1 & Customer2 capture cricket score message and Customer3 & Customer4 capture latest news message).

Implementation with Event

Oneway to achieve Publisher/Subscriber pattern in application is make use of Event/Delegate i.e. with the help of framework. Below is detail description of publisher/subscriber implementation

Message - Below is class which represent message that is publish by Publisher and capture by interested Subscriber.

C#
public class MessageArgument<T> : EventArgs
    {
        public T Message { get;  private set; }
        public MessageArgument(T message)
        {
            Message = message;
        }
    }

In technical term MessageArgument is generic class so instance of this class can be of any type which is represent by T template type.

Publisher  - as already described above in definition, Publisher is responsible for publish message of diffrent types.

C#
public interface IPublisher<T>
{
    event EventHandler<MessageArgument<T>> DataPublisher;
    //void OnDataPublisher(MessageArgument<T> args);
    void PublishData(T data);
}

public class Publisher<T> : IPublisher<T>
{
    //Defined datapublisher event
    public event EventHandler<MessageArgument<T>> DataPublisher;

    private void OnDataPublisher(MessageArgument<T> args)
    {
        var handler = DataPublisher;
        if (handler != null)
            handler(this, args);
    }


    public void PublishData(T data)
    {
        MessageArgument<T> message = (MessageArgument<T>)Activator.CreateInstance(typeof(MessageArgument<T>), new object[] { data });
        OnDataPublisher(message);
    }
}

Technically Publisher is generic class which inherit IPublisher interface, as Publisher is generic class instance of it can be of any type which is represented by T template type. Publisher Instance of created of selected given type publish on that type of message only. For publishing different type message it requires to create  different type of Publisher Instance. To understand better read below how to use publisher in client class code.

Publisher Class provide event DataPublisher , subscriber attach themselves to this event to listen the message.

PublishData is publisher class method that publish data to Subscribers.

Subscriber - Subscriber captures message of the type it interested in.

C#
public class Subscriber<T>
 {
     public IPublisher<T> Publisher { get; private set; }
     public Subscriber(IPublisher<T> publisher)
     {
         Publisher = publisher;
     }
 }

Technically Subscriber is generic class allows to create multiple instance of subscriber and each subscriber subscribe to message it interested in using publisher.

Subscriber pass instance of particular type publisher to capture message publisher by that Publisher.

How it works

C#
public class Client
{
    private readonly IPublisher<int> IntPublisher;
    private readonly Subscriber<int> IntSublisher1;
    private readonly Subscriber<int> IntSublisher2;

    public Client()
    {
        IntPublisher = new Publisher<int>();//create publisher of type integer

        IntSublisher1 = new Subscriber<int>(IntPublisher);//subscriber 1 subscribe to integer publisher
        IntSublisher1.Publisher.DataPublisher += publisher_DataPublisher1;//event method to listen publish data

        IntSublisher2 = new Subscriber<int>(IntPublisher);//subscriber 2 subscribe to interger publisher
        IntSublisher2.Publisher.DataPublisher += publisher_DataPublisher2;//event method to listen publish data

        IntPublisher.PublishData(10); // publisher publish message
    }

    void publisher_DataPublisher1(object sender, MessageArgument<int> e)
    {
        Console.WriteLine("Subscriber 1 : " + e.Message);
    }

    void publisher_DataPublisher2(object sender, MessageArgument<int> e)
    {
        Console.WriteLine("Subscriber 2 : " + e.Message);
    }
}

As you can see in the above code Client is class which create publisher and subscriber. Client class creates Publisher of integer type (in practice you can create any time of publisher) and creates two Subscriber classes which subscribe to the publisher message by attaching DataPublisher event which is provide by Publisher class.

So when you care instance of Client class you will receive following output

So as per the output Publisher of integer type published message "10" and two Subscriber who subscribed to publisher capture and displays message to output.

In practical scenario i.e. in actual application, one need to create all publisher at application start point i.e. at entrypoint of app and pass instance of publisher when creating subscriber. 

For Example in Windows application create publisher in Main() Method , in Web Application create publisher in Appication_Start method of Global.asax or make use of Dependency Injection in which you register your publisher and use container to create when needed.

Once created you can pass the publisher in Subscriber as done in above client class code.

Implementation with EventAggregator

EventAggregator - by name one can easily say that it aggregate events. In Publisher/Subscriber EventAggregator is woks as HUB whose task is to aggregate all publish message and send message to interested subscribers.

As you can see in above image EventAggregator comes as HUB between publisher and Subscriber, it works like this

  1. Publisher publish message
  2. EventAggregator Receives message send by publishers
  3. EventAggregator get list of all subscriber interested message
  4. EventAgregator sends message to interested subscriber

EventAggregator Implementation

Subscription - It class which used to create subscription token. When Subscriber subscribe to interested message type via EventAggregator return Subscription token which further used by Subscriber to keep track of its subscription. 

C#
//Does used by EventAggregator to reserve subscription
   public class Subscription<Tmessage> : IDisposable
   {
       public readonly MethodInfo MethodInfo;
       private readonly EventAggregator EventAggregator;
       public readonly WeakReference TargetObjet;
       public readonly bool IsStatic;

       private bool isDisposed;
       public Subscription(Action<Tmessage> action, EventAggregator eventAggregator)
       {
           MethodInfo = action.Method;
           if (action.Target == null)
               IsStatic = true;
           TargetObjet = new WeakReference(action.Target);
           EventAggregator = eventAggregator;
       }

       ~Subscription()
       {
           if (!isDisposed)
               Dispose();
       }

       public void Dispose()
       {
           EventAggregator.UnSbscribe(this);
           isDisposed = true;
       }

       public Action<Tmessage> CreatAction()
       {
           if (TargetObjet.Target!=null && TargetObjet.IsAlive )
               return (Action<Tmessage>)Delegate.CreateDelegate(typeof(Action<Tmessage>), TargetObjet.Target, MethodInfo);
           if(this.IsStatic)
               return (Action<Tmessage>)Delegate.CreateDelegate(typeof(Action<Tmessage>), MethodInfo);

           return null;
       }
   }

EventAggregator -

C#
public class EventAggregator
    {
        private readonly object lockObj = new object();
        private Dictionary<Type, IList> subscriber;

        public EventAggregator()
        {
            subscriber = new Dictionary<Type, IList>();
        }

        public void Publish<TMessageType>(TMessageType message)
        {
            Type t = typeof(TMessageType);
            IList sublst;
            if (subscriber.ContainsKey(t))
            {
                lock (lockObj)
                {
                    sublst = new List<Subscription<TMessageType>>(subscriber[t].Cast<Subscription<TMessageType>>());
                }

                foreach (Subscription<TMessageType> sub in sublst)
                {
                    var action = sub.CreatAction();
                    if (action != null)
                        action(message);
                }
            }
        }

        public Subscription<TMessageType> Subscribe<TMessageType>(Action<TMessageType> action)
        {
            Type t = typeof(TMessageType);
            IList actionlst;
            var actiondetail = new Subscription<TMessageType>(action, this);

            lock (lockObj)
            {
                if (!subscriber.TryGetValue(t, out actionlst))
                {
                    actionlst = new List<Subscription<TMessageType>>();
                    actionlst.Add(actiondetail);
                    subscriber.Add(t, actionlst);
                }
                else
                {
                    actionlst.Add(actiondetail);
                }
            }

            return actiondetail;
        }

        public void UnSbscribe<TMessageType>(Subscription<TMessageType> subscription)
        {
            Type t = typeof(TMessageType);
            if (subscriber.ContainsKey(t))
            {
                lock (lockObj)
                {
                    subscriber[t].Remove(subscription);
                }
                subscription = null;
            }
        }

    }

In above code

Dictionary<Type, IList> subscriber - is dictionary in which Type is Type of message and IList is list of action. So it holds list of action mapped to particular Message Type.

public void Publish<TMessageType>(TMessageType message) - is method used to publishing message. As in code this method does receives message as input than it filter out list of all subscriber by message type and publish message to Subscriber.

public Subscription<TMessageType> Subscribe<TMessageType>(Action<TMessageType> action) - is method used to subscribe interested message type. As in code this method receives Action delegate as input. It maps Action to particular MessageType, i.e. it create entry for message type if not present in dictionary and maps Subscription object  (which waps Action) to message entry.

public void UnSbscribe<TMessageType>(Subscription<TMessageType> subscription) - is method used to unsubscribe form particular message type. It receives Subscription object as input and remove object from the dictionary.

How To Use

C#
static void Main(string[] args)
  {
      EventAggregator eve = new EventAggregator();
      Publisher pub = new Publisher(eve);
      Subscriber sub = new Subscriber(eve);

      pub.PublishMessage();

      Console.ReadLine();

  }

As in above code shows it first create instance of EventAggregator which pass as argument to publisher and subscriber, than publisher publish message.

Publisher - Code of Publisher class, which shows how publisher publish message using EventAggregator

public class Publisher
   {
       EventAggregator EventAggregator;
       public Publisher(EventAggregator eventAggregator)
       {
           EventAggregator = eventAggregator;
       }

       public void PublishMessage()
       {
           EventAggregator.Publish(new Mymessage());
           EventAggregator.Publish(10);
       }
   }

Subscriber - Code of Subscriber class, which shows subscriber subscribe to messages which it interested in using EventAggregator.

public class Subscriber
{
    Subscription<Mymessage> myMessageToken;
    Subscription<int> intToken;
    EventAggregator eventAggregator;

    public Subscriber(EventAggregator eve)
    {
        eventAggregator = eve;
        eve.Subscribe<Mymessage>(this.Test);
        eve.Subscribe<int>(this.IntTest);
    }

    private void IntTest(int obj)
    {
        Console.WriteLine(obj);
        eventAggregator.UnSbscribe(intToken);
    }

    private void Test(Mymessage test)
    {
        Console.WriteLine(test.ToString());
        eventAggregator.UnSbscribe(myMessageToken);
    }
}

Output

Note:

In practical scenario i.e. in actual application, one need to create EventAggregator at application start point i.e. at entrypoint of app and pass instance of publisher and subscriber. 

For Example in Windows application create EventAggregator in Main() Method as in above example code, in Web Application create publisher in Appication_Start method of Global.asax or make use of Dependency Injection in which you register your publisher and use container to create when needed.

Event/Delegate Vs. EventAggregator

Difference between Event/Delegate and EventAggregator is

Event/Delegate EventAggregator

For publishing Different type of message there is need of creating different type of Publisher

For Example : Publisher<int> - for integer type publisher Publisher<string> - for string type publisher So the if class want to publish different type of messages it require to consume or create different type of publisher.

<int><string>To publish integer abd string type message publisher class will be Publisher (Publisher<int> intPublisher, Publisher<string> strPublisher).

As EventAggregator works as HUB there is no need create more than one instance of Eventaggregator and publisher just need to consume it. Example: Publisher(EventAggregator eventAggregator)
Tight Coupling bettwen Publisher and Subscriber. In this pattern Subscriber require to know publisher as they subscribe to event of publisher. Loose Coupling between Publisher and Subscriber. In this pattern as EventAggregator is mediator Publisher and Subscriber don’t know each other they just need to know EventAggregator.

 

Conclusion

It's my Point of view , Event/Delegate is easy to implement and good for the small project or in project where there are less number of Publisher/Subscriber. EventAggregator is suitable for the large project or project which is having large number of Publisher/Subscirber.

But I think its always good to use EventAggregator because its offer loose coupling.

 

NOTE

It's my point of view please do comment on this and provide your feedback. Thanks to Sacha Barber to helping in update.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India

Microsoft C# MVP (12-13)



Hey, I am Pranay Rana, working as a Team Leadin MNC. Web development in Asp.Net with C# and MS sql server are the experience tools that I have had for the past 5.5 years now.

For me def. of programming is : Programming is something that you do once and that get used by multiple for many years

You can visit my blog


StackOverFlow - http://stackoverflow.com/users/314488/pranay
My CV :- http://careers.stackoverflow.com/pranayamr

Awards:



Comments and Discussions

 
GeneralRe: My vote of 5 Pin
Sacha Barber19-Jan-15 22:55
Sacha Barber19-Jan-15 22:55 
Questionmemory leak Pin
Sacha Barber19-Jan-15 11:42
Sacha Barber19-Jan-15 11:42 
AnswerRe: memory leak Pin
Pranay Rana19-Jan-15 18:52
professionalPranay Rana19-Jan-15 18:52 
AnswerRe: memory leak Pin
Pranay Rana19-Jan-15 19:14
professionalPranay Rana19-Jan-15 19:14 
GeneralRe: memory leak Pin
Sacha Barber19-Jan-15 22:41
Sacha Barber19-Jan-15 22:41 
GeneralRe: memory leak Pin
Pranay Rana19-Jan-15 23:03
professionalPranay Rana19-Jan-15 23:03 
GeneralRe: memory leak Pin
Pranay Rana19-Jan-15 23:21
professionalPranay Rana19-Jan-15 23:21 
GeneralRe: memory leak Pin
Sacha Barber20-Jan-15 2:45
Sacha Barber20-Jan-15 2:45 
You would be on the right track there, but I think you would be better coming up with a WeakAction like I showed in the link, which has Target/Method etc etc

Also in your Publish method you would need to check the weak reference to see if it is still alive, and if it is not you can remove that subsciption. But be careful there too as there is a race condition, that could occur due to a context switch.

You should do something like this


bool alive = subscription.Action.IsAlive; //Action is a a new Property for the Action (I don't know what type you will make that)

if (!alive)
{
//remove subscription
}
else
{
//get subscription, possibly dynamically building new delegate on the fly, and invoke it
}

See what I mean. You can see an example of all of this in my Mediator class I posted link for, here is the code. This does everything you are trying to do, and more actually. Have a look see if you can borrow stuff perhaps:



C#
using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;

namespace Cinch
{


    #region WeakAction Inner Class
    /// <summary>
    /// This class creates a weak delegate of form Action(Of Object)
    /// </summary>
    public class WeakAction
    {
        #region Data
        private readonly WeakReference _target;
        private readonly Type _ownerType;
        private readonly Type _actionType;
        private readonly string _methodName;
        #endregion

        #region Public Properties/Methods
        public WeakAction(object target, Type actionType, MethodBase mi)
        {
            if (target == null)
            {
                Debug.Assert(mi.IsStatic);
                _ownerType = mi.DeclaringType;
            }
            else
                _target = new WeakReference(target);
            _methodName = mi.Name;
            _actionType = actionType;
        }

        public Type ActionType
        {
            get { return _actionType; }
        }

        public bool HasBeenCollected
        {
            get
            {
                return (_ownerType == null && (_target == null || !_target.IsAlive));
            }
        }

        public Delegate GetMethod()
        {
            if (_ownerType != null)
            {
                return Delegate.CreateDelegate(_actionType, _ownerType, _methodName);
            }

            if (_target != null && _target.IsAlive)
            {
                object target = _target.Target;
                if (target != null)
                    return Delegate.CreateDelegate(_actionType, target, _methodName);
            }

            return null;
        }
        #endregion
    }
    #endregion



    /// <summary>
    /// This class creates a simple Mediator which loosely connects different objects together.
    /// The message handlers are organized using string-based message keys and are held in a WeakReference
    /// collection.
    /// </summary>
    public class Mediator
    {


        #region Data
        static readonly Mediator instance = new Mediator();
        static readonly object syncLock = new object();
        private readonly Dictionary<object, List<WeakAction>> _registeredHandlers =
            new Dictionary<object, List<WeakAction>>();
        #endregion

        #region Ctor
        static Mediator()
        {

        }

        private Mediator()
        {

        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Performs the actual registration of a target
        /// </summary>
        /// <param name="key">Key to store in dictionary</param>
        /// <param name="actionType">Delegate type</param>
        /// <param name="handler">Method</param>
        private void RegisterHandler(object key, Type actionType, Delegate handler)
        {
            var action = new WeakAction(handler.Target, actionType, handler.Method);

            lock (_registeredHandlers)
            {
                List<WeakAction> wr;
                if (_registeredHandlers.TryGetValue(key, out wr))
                {
                    if (wr.Count > 0)
                    {
                        WeakAction wa = wr[0];
                        if (wa.ActionType != actionType &&
                            !wa.ActionType.IsAssignableFrom(actionType))
                            throw new ArgumentException("Invalid key passed to RegisterHandler - existing handler has incompatible parameter type");
                    }

                    wr.Add(action);
                }
                else
                {
                    wr = new List<WeakAction> { action };
                    _registeredHandlers.Add(key, wr);
                }
            }
        }

        /// <summary>
        /// Performs the unregistration from a target
        /// </summary>
        /// <param name="key">Key to store in dictionary</param>
        /// <param name="actionType">Delegate type</param>
        /// <param name="handler">Method</param>
        private void UnregisterHandler(object key, Type actionType, Delegate handler)
        {
            lock (_registeredHandlers)
            {
                List<WeakAction> wr;
                if (_registeredHandlers.TryGetValue(key, out wr))
                {
                    wr.RemoveAll(wa => handler == wa.GetMethod() && actionType == wa.ActionType);

                    if (wr.Count == 0)
                        _registeredHandlers.Remove(key);
                }
            }
        }

        /// <summary>
        /// This method broadcasts a message to all message targets for a given
        /// message key and passes a parameter.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="message">Message parameter</param>
        /// <returns>True/False if any handlers were invoked.</returns>
        private bool NotifyColleagues(object key, object message)
        {
            List<WeakAction> wr;
            List<WeakAction> wrCopy=new List<WeakAction>();
            lock (_registeredHandlers)
            {
                if (!_registeredHandlers.TryGetValue(key, out wr))
                    return false;
                else
                {
                    foreach (var weakRe in wr)
                    {
                        wrCopy.Add(weakRe);
                    }
                }

            }

            foreach (var cb in wrCopy)
            {
                Delegate action = cb.GetMethod();

                if (action != null)
                    action.DynamicInvoke(message);
            }

            lock (_registeredHandlers)
            {
                wr.RemoveAll(wa => wa.HasBeenCollected);
            }

            return true;
        }
        #endregion

        #region Public Properties/Methods

        /// <summary>
        /// Singleton Instance
        /// </summary>
        public static Mediator Instance
        {
            get
            {
                return instance;
            }
        }

        /// <summary>
        /// This registers a Type with the mediator.  Any methods decorated with <seealso cref="MediatorMessageSinkAttribute"/> will be
        /// registered as target method handlers for the given message key.
        /// </summary>
        /// <param name="view">Object to register</param>
        public void Register(object view)
        {
            // Look at all instance/static methods on this object type.
            foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
            {
                // See if we have a target attribute - if so, register the method as a handler.
                foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true))
                {
                    var mha = (MediatorMessageSinkAttribute)att;
                    var pi = mi.GetParameters();
                    if (pi.Length != 1)
                        throw new InvalidCastException("Cannot cast " + mi.Name + " to Action<T> delegate type.");

                    Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType);
                    object key = (mha.MessageKey) ?? actionType;

                    if (mi.IsStatic)
                        RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi));
                    else
                        RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name));
                }
            }
        }

        /// <summary>
        /// This method unregisters a type from the message mediator.
        /// </summary>
        /// <param name="view">Object to unregister</param>
        public void Unregister(object view)
        {
            foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
            {
                foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true))
                {
                    var mha = (MediatorMessageSinkAttribute)att;
                    var pi = mi.GetParameters();
                    if (pi.Length != 1)
                        throw new InvalidCastException("Cannot cast " + mi.Name + " to Action<T> delegate type.");

                    Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType);
                    object key = (mha.MessageKey) ?? actionType;

                    if (mi.IsStatic)
                        UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi));
                    else
                        UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name));
                }
            }
        }

        /// <summary>
        /// This registers a specific method as a message handler for a specific type.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="handler">Handler method</param>
        public void RegisterHandler<T>(string key, Action<T> handler)
        {
            RegisterHandler(key, handler.GetType(), handler);
        }

        /// <summary>
        /// This registers a specific method as a message handler for a specific type.
        /// </summary>
        /// <param name="handler">Handler method</param>
        public void RegisterHandler<T>(Action<T> handler)
        {
            RegisterHandler(typeof(Action<T>), handler.GetType(), handler);
        }

        /// <summary>
        /// This unregisters a method as a handler.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="handler">Handler</param>
        public void UnregisterHandler<T>(string key, Action<T> handler)
        {
            UnregisterHandler(key, handler.GetType(), handler);
        }

        /// <summary>
        /// This unregisters a method as a handler for a specific type
        /// </summary>
        /// <param name="handler">Handler</param>
        public void UnregisterHandler<T>(Action<T> handler)
        {
            UnregisterHandler(typeof(Action<T>), handler.GetType(), handler);
        }

        /// <summary>
        /// This method broadcasts a message to all message targets for a given
        /// message key and passes a parameter.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="message">Message parameter</param>
        /// <returns>True/False if any handlers were invoked.</returns>
        public bool NotifyColleagues<T>(string key, T message)
        {
            return NotifyColleagues((object)key, message);
        }

        /// <summary>
        /// This method broadcasts a message to all message targets for a given parameter type.
        /// If a derived type is passed, any handlers for interfaces or base types will also be
        /// invoked.
        /// </summary>
        /// <param name="message">Message parameter</param>
        /// <returns>True/False if any handlers were invoked.</returns>
        public bool NotifyColleagues<T>(T message)
        {
            Type actionType = typeof(Action<>).MakeGenericType(typeof(T));
            var keyList = from key in _registeredHandlers.Keys
                          where key is Type && ((Type)key).IsAssignableFrom(actionType)
                          select key;
            bool rc = false;
            foreach (var key in keyList)
                rc |= NotifyColleagues(key, message);

            return rc;
        }

        /// <summary>
        /// This method broadcasts a message to all message targets for a given
        /// message key and passes a parameter.  The message targets are all called
        /// asynchronously and any resulting exceptions are ignored.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="message">Message parameter</param>
        public void NotifyColleaguesAsync<T>(string key, T message)
        {
            Func<string, T, bool> smaFunc = NotifyColleagues;
            smaFunc.BeginInvoke(key, message, ia =>
            {
                try { smaFunc.EndInvoke(ia); }
                catch { }
            }, null);
        }

        /// <summary>
        /// This method broadcasts a message to all message targets for a given parameter type.
        /// If a derived type is passed, any handlers for interfaces or base types will also be
        /// invoked.  The message targets are all called asynchronously and any resulting exceptions
        /// are ignored.
        /// </summary>
        /// <param name="message">Message parameter</param>
        public void NotifyColleaguesAsync<T>(T message)
        {
            Func<T, bool> smaFunc = NotifyColleagues;
            smaFunc.BeginInvoke(message, ia =>
            {
                try { smaFunc.EndInvoke(ia); }
                catch { }
            }, null);
        }
        #endregion
    }
}

GeneralRe: memory leak Pin
Pranay Rana20-Jan-15 5:36
professionalPranay Rana20-Jan-15 5:36 
GeneralRe: memory leak Pin
Sacha Barber20-Jan-15 7:25
Sacha Barber20-Jan-15 7:25 
GeneralRe: memory leak Pin
Pranay Rana20-Jan-15 8:33
professionalPranay Rana20-Jan-15 8:33 
GeneralRe: memory leak Pin
Sacha Barber20-Jan-15 9:22
Sacha Barber20-Jan-15 9:22 
GeneralRe: memory leak Pin
Pranay Rana20-Jan-15 9:48
professionalPranay Rana20-Jan-15 9:48 
GeneralRe: memory leak Pin
Sacha Barber20-Jan-15 10:48
Sacha Barber20-Jan-15 10:48 
GeneralRe: memory leak Pin
oniDino2-Mar-18 11:19
oniDino2-Mar-18 11:19 
GeneralAwesome article Pin
Jean-Francois Gouin19-Jan-15 8:59
Jean-Francois Gouin19-Jan-15 8:59 
GeneralRe: Awesome article Pin
Pranay Rana19-Jan-15 18:51
professionalPranay Rana19-Jan-15 18:51 
Question5 for me! Pin
Armando Airo'19-Jan-15 2:31
Armando Airo'19-Jan-15 2:31 
AnswerRe: 5 for me! Pin
Pranay Rana19-Jan-15 3:30
professionalPranay Rana19-Jan-15 3:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.