Friday, October 07, 2005

Interested in me ? Subscribe please !

The contents of this blog may not interest you as did its title, because its a technical one. I have decided to include more technical articles (inspite of the fact that there are gazillion tech blogs out there) not because I think this one is going to be better than them but just because it would help me understand things in a better way. So my technical rants are just for my own benefit. If anyone else gets something useful out of these blogs, that would be a real pleasant bonus.

I was also confused as to how my approach should be. Should I write it in such a way that non-techies would also understand it ? Or should I aim at the developers who have atleast a general idea of what a class is? Although I would like to go the first way, there are atleast three reasons I am forced to go with the second option.
1)I dont think non-techies would like to dig deep into the .NET trenches irrespective of the way I present things.
2)Most of the people who read my blog are technical people.
3)I wouldnt be able to do justice to the real topic at hand, if I have to write so much explaining the context to the layman which is so painfully obvious to techies.

The title of this blog might have given a clue to what this blog is going to be. Its about programming with events. I just would like to assume that you would be knowing what an event is. If you dont, there aint much use reading the rest of this anyway.

Events are a convenience for modern programming. I will show here how to implement event handling mechanism in C#. Delegates are an integral part of the .NET framework and play a key role in the event handling mechanism. Put simply, delegates are type-safe function pointers.

Let us assume there is a Publisher class which raise an event and a Subscriber class is interested in that event.

using System;

class Publisher
{
private int _state = 0;

public delegate void StateChangeEventHandler(Object sender,EventArgs args);
public event StateChangeEventHandler StateChanged;

public int State
{
get
{
return _state;
}
set
{
_state = value;
if(StateChanged != null)
StateChanged(this,EventArgs.Empty);
}
}
}

The above code can be explained as follows. The publisher class contains 2 key members which allows it to raise events. One is a delegate and the other is the event. The delegate specifies the signature for the event handler methods. Two conventions are usually followed here. First one is regarding the delegate name - it should end with EventHandler. The convention for the event handler signature is to have 2 parameters and the return type as void. The first parameter will be the object that raises the event (the source of the event or in other words, an instance of the publisher). The second parameter will be an instance of a System.EventArgs class or a class that derives from it. There is a well defined logic in this convention. The obvious advantage is the resulting consistency among event handlers. Also, the choice of the first parameter as the event source allows a single event handler to handle events from multiple sources. The event handler can examine the parameter to determine which object raised the event. The requirement for this to work is that the signature specified by the 2 delegates should be same.

In our Publisher class we have an event called StateChanged which will be raised when the State property changes. To raise the event we use this code :


StateChanged(this,new EventArgs());

But the problem is that if no objects have subscribed to this event, you will get a NullReferenceException. To prevent that we need a check :


if(StateChanged != null)
//raise event


Usually we would keep the above code in a separate method like:

private void OnStateChanged(EventArgs e)
{
if(StateChanged != null)
StateChanged(this,e);
}

For additional flexibility we can make this method protected virtual so that derived types can have some control over the firing of events.

protected virtual void OnStateChanged(EventArgs e)
{
if(StateChanged != null)
StateChanged(this,e);
}

Now our Publisher class will look like this :

using System;

class Publisher
{
private int _state = 0;

public delegate void StateChangeEventHandler(Object sender,EventArgs args);
public event StateChangeEventHandler StateChanged;

public int State
{
get
{
return _state;
}
set
{
_state = value;
OnStateChanged(EventArgs.Empty);
}
}

protected virtual void OnStateChanged(EventArgs e)
{
if(StateChanged != null)
StateChanged(this,e);
}

}//~end of Publisher class


Now that we know how to publish and raise events, we can look at how to setup a subscriber.

class Subscriber
{

public void Subscribe(Publisher pub)
{
pub.StateChanged += new Publisher.StateChangeEventHandler(pub_StateChanged);
}

public void UnSubscribe(Publisher pub)
{
pub.StateChanged -= new Publisher.StateChangeEventHandler(pub_StateChanged);
}

private void pub_StateChanged(Object sender,EventArgs args)
{
Console.WriteLine(sender.GetType().ToString() + "'s State Changed !");
}
}//~end of Subscriber class


The code is mostly self explanatory. The Subscribe and UnSubscribe methods show how we can subscribe to and unscribe from events using the += and -= overloaded operators. For the eventhandler the name of the method can be anything (the convention is 'object_event') but the signature of the event handler should conform to the signature specified by the delegate.

We can have static events if we so prefer. If you subscribe to a static event, you will receive notifications from all instances of the Publisher class and if you unsubscribe you will recieve notifications from none. You dont need an instance of the Publisher class to subscribe to a static event. Note that nothing prevents the eventhandler function itself from being a static function.

As a point of interest, it should be mentioned that an event is implemented by a multicast delegate. To handle multiple subscribers it would internally maintain a list of subscribers.

If you didnt guess it till now, the event mechanism follows the Observer design pattern (see Design Patterns by the GoF).

P.S. Sorry about the formatting, I tried my best though :-)

1 Comments:

Anonymous Binil Thomas said...


So my technical rants are just for my own benefit. If anyone else gets something useful out of these blogs, that would be a real pleasant bonus.


Man, you should explore what is known as "blog categories".

10/17/2005 11:58:00 AM  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home