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:

// C#
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);
    }
}
' Visual Basic
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:

// C#
public delegate void TimerEventHandler(object sender, EventArgs eventArgs);
' Visual Basic
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:

// C#
public event TimerEventHandler Started;
public event TimerEventHandler Elapsed;
' Visual Basic
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:

// C#
private void OnStarted(EventArgs eventArgs)
{
    if (Started != null)
        Started(this, eventArgs);
}

private void OnElapsed(EventArgs eventArgs)
{
    if (Elapsed != null)
        Elapsed(this, eventArgs);
}
' Visual Basic
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:

// C#
public void Start()
{
    Thread timerThread = new Thread
    (
        delegate()
        {
            OnStarted(new EventArgs());
            Thread.Sleep(time);
            OnElapsed(new EventArgs());
        }
    );
    timerThread.Start();
}
' Visual Basic
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:

// C#
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.");
}
' Visual Basic
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.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"