Posted by: Zeeshan Amjad | February 22, 2012

Using Mediator to communicate between user controls: Part 6


We developed our mediator class to handle lots of basic conditions, but it is still not perfect. There are still few problems in it. The first problem is passing the wrong data, such as null. All of these are not handled in mediator class.

Code Snippet
Mediator.Register<Student>(null, null);
Mediator.Register<Student>("mymessage1", null);
Mediator.Register<Student>(null, studentform.Notify);

 

The solution is quite simple. We changed our register and unregister method to check the null and throw an exception. Here is updated version of our class.

Code Snippet
public void Register<T>(string message, Action<T> action)
{
    if (message == null)
        throw new ArgumentNullException("message");

    if (action == null)
        throw new ArgumentNullException("action");

    if (!actions.ContainsKey(message))
    {
        actions[message] = new List<Delegate>();
    }
    
    actions[message].Add(action);
}

public void UnRegister<T>(string message, Action<T> action)
{
    if (message == null)
        throw new ArgumentNullException("message");

    if (action == null)
        throw new ArgumentNullException("action");

    if (actions.ContainsKey(message))
    {
        List<Delegate> actionlist = actions[message];

        actionlist.Remove(action);

        if (actionlist.Count == 0)
            actions.Remove(message);
    }
}

 

The other problem is actually a bigger one. This class can not be used safely in multi threaded environment.

In multi threaded environment it is important to protect our data structure, actions in our example, to access from more than one thread at a time. We have to use some thread synchronization object because this is not an atomic operation. According to the CLI specification section 12.6.6, Partition I.

We can use the Monitor class and use Monitor.Enter and Monitor.Exit method. C# provide very nice syntactical sugar for it by introducing the lock key work. C# compiler generates the code that uses the Monitor class internally. we changed all methods of our mediator class to use the lock keywords whenever they are accessing the actions field.

Here is an updated version of our Mediator class.

Code Snippet
using System;
using System.Collections.Generic;

namespace MediatorUserControl
{
    public class Mediator
    {
        private Dictionary<string, List<Delegate>> actions =
            new Dictionary<string, List<Delegate>>();

        public Mediator()
        {
        }

        public void Register<T>(string message, Action<T> action)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            if (action == null)
                throw new ArgumentNullException("action");

            lock (actions)
            {
                if (!actions.ContainsKey(message))
                {
                    actions[message] = new List<Delegate>();
                }

                actions[message].Add(action);
            }
        }

        public void UnRegister<T>(string message, Action<T> action)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            if (action == null)
                throw new ArgumentNullException("action");

            lock (actions)
            {
                if (actions.ContainsKey(message))
                {
                    List<Delegate> actionlist = actions[message];

                    actionlist.Remove(action);

                    if (actionlist.Count == 0)
                        actions.Remove(message);
                }
            }
        }

        public void Send<T>(string message, T param)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            lock (actions)
            {
                if (actions.ContainsKey(message))
                {
                    List<Delegate> actionslist = actions[message];

                    foreach (var action in actionslist)
                    {
                        action.DynamicInvoke(param);
                    }
                }
            }
        }
    }
}

 

Note here we are using lock whenever we are trying to access the actions field.

Rest of the classes are exactly the same in this project and there is no change required to use this class. There is even no change in the behavior and the output of this program is exactly same as previous program.

mediator_02_output

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: