Thursday, December 20, 2007

Derived classes and events

Events are a useful feature of the C# language and allow for clients to be notified when the state of a server changes. You create an event by declaring it in the class that will raise the event:

public event EventHandler NotifyClient;

Here, I used the generic event template to eliminate the need to define a separate delegate. To register a callback method with the event, we do the following in the class that will receive the event notification from the class that raised the event:

message.NotifyClient += new EventHandler(message_NotifyClient);

static void message_NotifyClient(object sender, EventArgs e)
{
throw new Exception("The method or operation is not implemented.");
}

When the event is raised the message_NotifyClient method will execute. To raise the event we add the following to the class that raises the event:

protected virtual void OnNotifyClient(EventArgs e)
{
EventHandler handler = NotifyClient;

if (handler != null)
handler(this, e);
}

Here, we make a copy of the event since access to the event is not thread-safe (i.e. another thread could delete/modify the event before the if (handler != null) completes). When the client wants to raise the event, it does so by executing the OnNotifyClient method:

private void DoSomething()
{
// Do something...
OnNotifyClient(new EventArgs());
}

When OnNotifyClient is executed, the message_NotifyClient method is executed.

Derived classes and events
An event can only be raised from methods declare within the class that declared the event. So events declared in class A can only be raised from methods within class A. By default, events are not inherited by derived classes, so in the following base class definition:

public class Base
{
public event EventHander NotifyClient;
}

with the derived class definition

public class Derived : Base
{
private void DoSomething()
{
EventHandler handler = NotifyClient;

if (handler != null)
handler(this, new EventArgs());
}
}

The derived class will not have access to the NotifyClient event since the NotifyClient event was not declared in the Derived class. There are two ways to workaround this requirement:
You'll notice above that the OnNotifyClient method is declared with the virtual keyword. This allows the Derived class to override the OnNotifyClient method and access the NotifyClient directly. So, we could do this in the Derived class:

protected override void OnNotifyClient(EventArgs e)
{
EventHandler handler = NotifyClient;

if (handler != null)
handler(this, e);
}

protected void DoSomething()
{
// Do something Derived-class specific, then call OnNotifyClient
OnNotifyClient(new EventArgs());
}

The second approach is to declare the event virtual in the Base class, then override the event in the Derived class:

public class Base
{
public virtual event EventHandler NotifyClient;
...
}

public class Derived : Base
{
public override event EventHandler NotifyClient;

public void DoSomething()
{
EventHandler handler = NotifyClient;

// Here, we can access the NotifyClient directly since we've overridden access
if (handler != null)
handler(this, new EventArgs());
}
}

Which approach you take is up to you. Both approaches give the Derived class the flexibility to perform Derived-specific tasks around the decision to raise the event. It really comes down to which approach is preferred.

This page is powered by Blogger. Isn't yours?