This article describes a very important and one of the powerful tools in C#.
This is based on a famous design pattern called Observer. Observer design pattern dictates what is called as subscriber-publisher paradigm. An example will make it clear.Lets say we are building a 3 tier architecture where there are several middle layer business objects. BO_1 BO_2 BO_3 etc. All these business objects provide some services and fire some notifications. The design pattern suggests that these BOs need not to know about the existence of each other or anyone else for that matter except one central controller or manager.The manager is responsible for getting events from different BOs and then route those events to interested clients.For example BO_1 may be interested in events from BO_2.It needs to register for events with central Manager CM.CM is responsible for sending events to all interested clients.When BO_1 detects some change in its state it notifies CM and CM walks through a list of clients which had registered for those events and calls the methods (also called callback) on each of the clients.
What does this have to do with C#?
Well,C# provides built in notification mechanism through delegate functions.This article will illustrate the same.Before you really start reading this article any further,please refer to the article on delegates
Once you know what delegates are understanding this article and hence a powerful paradigm is a piece of cake.To recall,delegates are equivalent to function pointers.
Lets take a scenario .Suppose we have a collection which has several methods including AddItem().Lets say there are several Business objects which want to get notified whenever a new item is added to collection so that they can do some things like update their internal data structures.
In our example we will need to have a Collection class which we will call CCollection. In addition to that we will have another class BusinessObject which will have a CCollection object in it and will be interested in knowing when the collection changed.
So lets try to design this in C#.We first need to define CCollection class which will have a method called AddItem which will be called to add a new item to the collection.The AddItem takes a key and a value as the parameter.So whenever a AddItem is called on Collection, all clients need to know about this.In other words the collection class needs to fire a notification to all its clients.How does it do so? The answer lies in delegate methods.First of all the Collection needs to publishes or define a method prototype which has two parameters : first parameter is of the type object and the second parameter needs to be the data that is passed along with the notification.The second parameter needs to be a class derived from a system class called EventArgs.This is important .So we will first define a class called CEvent which is derived from EventArgs. CCollection class sends this object along with the notification.Here is the definition of CEvent.
class CItem:EventArgs
{
public readonly string m_cszKey;
public readonly long m_lValue;
public CItem(string strKey,long lVal)
{
m_cszKey = strKey;
m_lValue = lVal;
}
}
Note that the class has only two variables which are readonly which mean they can be changed one time.The collection class passes the key and the value to its clients.So in the AddItem method of CCollection class we will call the OnAddItem which is implemented by the clients who want to get notifications.Following is the code for CCollection class:
class CCollection
{
public delegate void AddNewItem(object theObj,
CItem theItem);
public event AddNewItem OnAddItem;
public void AddItem(string theKey,long theVal)
{
CItem theRCItem = new CItem (theKey,theVal);
if(OnAddItem != null )
{
OnAddItem(this,theRCItem);
}
}
}
Next we need to define a client class :As we said in the begining of this article that clients need to communicate to the the server that they are interested in certain type of events.And need to pass a callback method so that server can call that method on client.How this is done is that client needs to provide an implementation of the delegate method and tell sever about that method.If you look at the code below the client BusinessObject first gives the defintion of delegate method that server is supposed to call then in the constructor ,it adds this method(pointer to function ,for C++ analogy) to the list of callback methods of server by calling
m_rcColl.OnAddItem += new CCollection .AddNewItem (OnAddItem);
Note the "+=" operator .
Following is the client class.
class BusinessObject
{
CCollection m_rcColl;
string m_BO;
public void OnAddItem(object theObj, CItem theItem)
{
Console.WriteLine ("[{2}]>>>New Item Added to the collection: Key = {0} Value = {1}",
theItem.m_cszKey,theItem.m_lValue,m_BO);
}
public BusinessObject( CCollection theColl,string theName)
{
m_BO = theName;
m_rcColl = theColl;
m_rcColl.OnAddItem += new CCollection .AddNewItem (OnAddItem);
}
}
To add all up here is the resultant code:
namespace WS
{
using System;
class TestNotify
{
public static void Main()
{
CCollection theColl = new CCollection() ;
BusinessObject theBO1 = new BusinessObject (theColl,"WO");
BusinessObject theBO2 = new BusinessObject (theColl,"LIS BO");
theColl.AddItem ("Ashish",6674);
}
class CItem:EventArgs
{
public readonly string m_cszKey;
public readonly long m_lValue;
public CItem(string strKey,long lVal)
{
m_cszKey = strKey;
m_lValue = lVal;
}
}
class CCollection
{
public delegate void AddNewItem(object theObj,
CItem theItem);
public event AddNewItem OnAddItem;
public void AddItem(string theKey,long theVal)
{
CItem theRCItem = new CItem (theKey,theVal);
if(OnAddItem != null )
{
OnAddItem(this,theRCItem);
}
}
}
class BusinessObject
{
CCollection m_rcColl;
string m_BO;
public void OnAddItem(object theObj, CItem theItem)
{
Console.WriteLine ("[{2}]>>>New Item Added to the collection: Key = {0} Value = {1}",
theItem.m_cszKey,theItem.m_lValue,m_BO);
}
public BusinessObject( CCollection theColl,string theName)
{
m_BO = theName;
m_rcColl = theColl;
m_rcColl.OnAddItem += new CCollection .AddNewItem (OnAddItem);
}
}
}
}