Introduction
So, here we are at the final part of this series on delegates; and it wouldn't be complete without looking at one of the key uses of delegates in .NET: Event handling.
A Simple Timer
In this article, we are going to look at a simple timer example. This timer raises two events: one when the timer is started and another when the timer has elapsed. The complete code for the timer is as follows:
public delegate void TimerEventHandler(object sender, EventArgs eventArgs);
public class SimpleTimer
{
private TimeSpan time;
public event TimerEventHandler Started;
public event TimerEventHandler Elapsed;
public SimpleTimer(long milliseconds)
: this(TimeSpan.FromMilliseconds(milliseconds))
{
}
public SimpleTimer(TimeSpan time)
{
this.time = time;
}
public void Start()
{
Thread timerThread = new Thread
(
delegate()
{
OnStarted(new EventArgs());
Thread.Sleep(time);
OnElapsed(new EventArgs());
}
);
timerThread.Start();
}
private void OnStarted(EventArgs eventArgs)
{
if (Started != null)
Started(this, eventArgs);
}
private void OnElapsed(EventArgs eventArgs)
{
if (Elapsed != null)
Elapsed(this, eventArgs);
}
}
Public Delegate Sub TimerEventHandler_
(ByVal sender As Object, ByVal eventArgs As EventArgs)
Public Class SimpleTimer
Private _time As TimeSpan
Public Event Started As TimerEventHandler
Public Event Elapsed As TimerEventHandler
Public Sub New(ByVal milliseconds As Long)
Me.New(TimeSpan.FromMilliseconds(milliseconds))
End Sub
Public Sub New(ByVal time As TimeSpan)
_time = time
End Sub
Public Sub Start()
Dim timerThread As Thread = New Thread _
( _
Sub()
OnStarted(New EventArgs())
Thread.Sleep(_time)
OnElapsed(New EventArgs())
End Sub _
)
timerThread.Start()
End Sub
Private Sub OnStarted(ByVal eventArgs As EventArgs)
RaiseEvent Started(Me, New EventArgs())
End Sub
Private Sub OnElapsed(ByVal eventArgs As EventArgs)
RaiseEvent Elapsed(Me, New EventArgs())
End Sub
End Class
Now let's look at this code a little more closely. Firstly, notice how we declare a delegate for any methods which can be used to handle our two events:
public delegate void TimerEventHandler(object sender, EventArgs eventArgs);
Public Delegate Sub TimerEventHandler(ByVal sender As Object, _
ByVal eventArgs As EventArgs)
By convention, event handlers accept two parameters:
Parameter | Description |
---|
sender | The object which raised the event |
eventArgs | An object of type EventArgs , or inherits from EventArgs , which contains encapsulates any optional data we wish to pass to our event handler |
We also declare the two events which our timer is going to raise:
public event TimerEventHandler Started;
public event TimerEventHandler Elapsed;
Public Event Started As TimerEventHandler
Public Event Elapsed As TimerEventHandler
Notice how the type of each of these events is that of the delegate which is going to handle them, in this case TimerEventHandler
.
We also have convenience methods for raising each of the events:
private void OnStarted(EventArgs eventArgs)
{
if (Started != null)
Started(this, eventArgs);
}
private void OnElapsed(EventArgs eventArgs)
{
if (Elapsed != null)
Elapsed(this, eventArgs);
}
Private Sub OnStarted(ByVal eventArgs As EventArgs)
RaiseEvent Started(Me, New EventArgs())
End Sub
Private Sub OnElapsed(ByVal eventArgs As EventArgs)
RaiseEvent Elapsed(Me, New EventArgs())
End Sub
Note how we fire the event in exactly the same way as we would call any other delegate. Additionally, in C#, we do a check to see if any handlers have been wired-up up to our event; as firing an event without any handlers will cause an exception to be thrown.
Finally, the code which actually runs our timer and raises the events. Note how the timer is executed in a separate thread and (simply because it is in-keeping with the theme of this series) is passed to the thread as an anonymous method:
public void Start()
{
Thread timerThread = new Thread
(
delegate()
{
OnStarted(new EventArgs());
Thread.Sleep(time);
OnElapsed(new EventArgs());
}
);
timerThread.Start();
}
Public Sub Start()
Dim timerThread As Thread = New Thread _
( _
Sub()
OnStarted(New EventArgs())
Thread.Sleep(_time)
OnElapsed(New EventArgs())
End Sub _
)
timerThread.Start()
End Sub
Using our Timer
The following code shows how to use our simple timer and hook up our own methods to handle the events it raises:
static void Main(string[] args)
{
SimpleTimer timer = new SimpleTimer(5000);
timer.Started += timer_Started;
timer.Elapsed += timer_Elapsed;
timer.Start();
Console.Read();
}
static void timer_Started(object sender, EventArgs e)
{
Console.WriteLine("Timer has started.");
}
static void timer_Elapsed(object sender, EventArgs e)
{
Console.WriteLine("Timer has elapsed.");
}
Sub Main(ByVal args() As String)
Dim timer As SimpleTimer = New SimpleTimer(5000)
AddHandler timer.Started, AddressOf timer_Started
AddHandler timer.Elapsed, AddressOf timer_Elapsed
timer.Start()
Console.Read()
End Sub
Private Sub timer_Started(sender As Object, eventArgs As EventArgs)
Console.WriteLine("Timer has started.")
End Sub
Private Sub timer_Elapsed(sender As Object, eventArgs As EventArgs)
Console.WriteLine("Timer has elapsed.")
End Sub
Note how the syntax for wiring up our event handlers to the events is slightly different from what we've seen previously. This is because events are a type of multicast delegate, and as such multiple handlers can be wired up the same event.
Summary
Handling of events is one of the key uses for delegates in .NET. All event handlers follow a specific convention in terms of their signature and multiple different handlers can be wired up to the same event.
CodeProject