Vous êtes sur la page 1sur 8

C# Delegates and Events

A delegate is a type that refers to a static method or an instance method.

A delegate is a .NET class that encapsulates a method, but not in the same way other classes
encapsulate methods. A delegate actually stores the address of a method that is contained in some
other class. So, a delegate is really the equivalent of a function pointer in C++. However, they are also
far more than that.

In this article, I explain the many uses of delegates. I begin with a simple example using a delegate to
invoke a method. Then, I show several other uses of delegates including multicast delegates, thread
delegates, anonymous methods, asynchronous method calls, events, and Win32 callbacks.

Declaring and using delegates


You declare a delegate in a class or namespace using the delegate keyword and by specifying the
signature of the method it will call. The following line declares a delegate named
GreetingDelegate which will reference a method that returns void and accepts a string as the
argument.

delegate void GreetingDelegate(string s);

Now that we have declared the GreetingDelegate, we can instantiate it to encapsulate a method
of that signature. Then, we can invoke the method through the delegate, just as if we invoked the
method itself.

The next code sample creates an instance of GreetingDelegate and uses it to invoke the
SendGreeting method.

class DelegateDemo1 {

static void Main(string[] args) {


GreetingDelegate gd = new GreetingDelegate(SendGreeting);
gd("Hello");
}

static void SendGreeting(string s) {


Console.WriteLine(s);
}
}
The DelegateDemo1 class shown above contains a static method named SendGreeting which
has the same signature as the GreetingDelegate defined earlier. The Main method instantiates
the GreetingDelegate, passing the SendGreeting method as argument. Next, Main invokes
the SendingGreeting method through the delegate instance, passing the string “Hello”.

Delegates only depend on the signature of the method, not on the class or object containing the
method. Although the SendGreeting method above was declared as static, this is not a
requirement. A delegate can reference an instance method as well. The next example uses the same
GreetingDelegate defined earlier to invoke an instance method.

class Greeting {
public void SendGreeting(string s) {
Console.WriteLine(s);
}
}

class DelegateDemo1 {
static void Main(string[] args) {
Greeting gr = new Greeting();
GreetingDelegate gd = new GreetingDelegate(gr.SendGreeting);
gd ("Hello");
}
}

This example defines a Greeting class that contains a single method named SendGreeting.
Because its signature matches that of the GreetingDelegate, the method can be invoked through
our delegate.

In this example, the Main method first instantiates the Greeting class. Then, it creates an instance
of GreetingDelegate, passing the Greeting object’s SendGreeting method as argument.
Finally, Main calls the object’s SendGreeting method by invoking the delegate.

Multicast delegates

As I said in the introduction, delegates do far more than provide an indirect way to call a method. They
also contain an internal list of delegates—called the invocation list—which contains delegates of the
same method signature. Whenever the delegate is invoked, it also invokes each delegate in its
invocation list. You add and remove delegates from an invocation list using the overloaded += and -=
operators.

The next example defines two methods of the correct signature for our GreetingDelegate and
calls them both through a single call to an instance of the GreetingDelegate.
class Greeting {
public void SendGreeting(string s) {
Console.WriteLine(s);
}

public void SendGreetingToFile(string s) {


StreamWriter writer =
new StreamWriter(@"c:\greeting.txt");
writer.WriteLine(s);
writer.Close();
}
}

class DelegateDemo1 {
static void Main(string[] args) {
// create the Greeting object
Greeting gr = new Greeting();

// create the delegates


GreetingDelegate gd = new GreetingDelegate(gr.SendGreeting);
gd += new GreetingDelegate(gr.SendGreetingToFile);

// invoke both methods through the delegate


gd("Hello");
}
}

In this example, we have added a second method to the Greeting class. The
SendGreetingToFile method does exactly as its name suggests. It creates a StreamWriter
instance and uses it to log the string argument to a file named "greeting.txt".

This time, after creating the GreetingDelegate, the Main method instantiates a second
GreetingDelegate and appends it to the first instances’ invocation list through the += operator.
When the last line of Main calls the delegate, both methods are invoked with the string “Hello”.

So far, we have seen delegates used to call methods of other classes and objects. But, as I have
already said, that is only the beginning. The fact that they can be bound to methods of any class or
object gives them great utility. Delegates are central to .NET, as they are used in threads, events, and
asynchronous programming. Now that we have seen how to create and use delegates, let’s take a
look at some of their applications.

Thread delegates
Multithreaded programming in .NET is implemented through the use of delegates. The .NET
Framework defines the ThreadStart delegate to invoke a method in a separate thread. The
Thread class, defined in the System.Threading namespace, accepts an instance of the
ThreadStart delegate as its constructor argument. When you call the Thread object’s Start
method, that Thread object will invoke the delegate in its own thread of execution. The following
example demonstrates this by calling a method named ThreadProc in a separate thread.

static void ThreadProc() {


// do some thread work
CalculatePrimes();
}

static void Main(string[] args) {


Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
t.Join();
}

In this example, the Main method creates a Thread object, passing a new ThreadStart delegate
to its constructor. That ThreadStart delegate stores a reference to the ThreadProc method.
Then, Main calls the Thread object’s Start method which causes its delegate to begin execution in
a separate thread. The final line of Main is a call to the Thread object’s Join method, which causes
Main to block, waiting for the thread’s delegated method call to return

Events
The Event model in C# finds its roots in the event programming model that is
popular in asynchronous programming. The basic foundation behind this
programming model is the idea of "publisher and subscribers." In this model, you
have publishers who will do some logic and publish an "event." Publishers will then
send out their event only to subscribers who have subscribed to receive the specific
event.

In C#, any object can publish a set of events to which other applications can
subscribe. When the publishing class raises an event, all the subscribed applications
are notified. The following figure shows this mechanism.
Conventions

The following important conventions are used with events:

Event Handlers in the .NET Framework return void and take two
parameters.
The first paramter is the source of the event; that is the publishing
object.
The second parameter is an object derived from EventArgs.
Events are properties of the class publishing the event.
The keyword event controls how the event property is accessed by the
subscribing classes.

Simple Event

Let's modify our logging example from above to use an event rather than a delegate:

using System;
using System.IO;
namespace Akadia.SimpleEvent
{
/* ========= Publisher of the Event ============== */
public class MyClass
{
// Define a delegate named LogHandler, which will encapsulate
// any method that takes a string as the parameter and returns
no value
public delegate void LogHandler(string message);

// Define an Event based on the above Delegate


public event LogHandler Log;

// Instead of having the Process() function take a delegate


// as a parameter, we've declared a Log event. Call the Event,
// using the OnXXXX Method, where XXXX is the name of the
Event.
public void Process()
{
OnLog("Process() begin");
OnLog("Process() end");
}

// By Default, create an OnXXXX Method, to call the Event


protected void OnLog(string message)
{
if (Log != null)
{
Log(message);
}
}
}

// The FileLogger class merely encapsulates the file I/O


public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;

// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}

// Member Function which is used in the Delegate


public void Logger(string s)
{
streamWriter.WriteLine(s);
}

public void Close()


{
streamWriter.Close();
fileStream.Close();
}
}

/* ========= Subscriber of the Event ============== */


// It's now easier and cleaner to merely add instances
// of the delegate to the event, instead of having to
// manage things ourselves
public class TestApplication
{
static void Logger(string s)
{
Console.WriteLine(s);
}

static void Main(string[] args)


{
FileLogger fl = new FileLogger("process.log");
MyClass myClass = new MyClass();

// Subscribe the Functions Logger and fl.Logger


myClass.Log += new MyClass.LogHandler(Logger);
myClass.Log += new MyClass.LogHandler(fl.Logger);

// The Event will now be triggered in the Process() Method


myClass.Process();

fl.Close();
}
}
}

Compile an test:

# csc SimpleEvent.cs
# SimpleEvent.exe
Process() begin
Process() end
# cat process.log
Process() begin
Process() end

More useful links to see.


http://msdn.microsoft.com/en-us/library/900fyy8e(VS.71).aspx (Delegates)
http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx (Events)
http://msdn.microsoft.com/en-us/library/17sde2xt(VS.71).aspx (Delegates & Events)
http://msdn.microsoft.com/en-us/library/system.delegate(VS.71).aspx (Raising an
Event)
http://msdn.microsoft.com/en-us/library/2ccyd347(VS.71).aspx (Consuming an
Event)

http://msdn.microsoft.com/en-us/vcsharp/bb508935.aspx (Multicast Delegate


Internals)
http://www.dotnetjohn.com/articles.aspx?articleid=262 (Working with Delegates
Delegates in
C#)
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=18 (Delegates and
Events - The Uncensored Story)
http://www.akadia.com/services/dotnet_delegates_and_events.html
http://en.csharp-online.net/CSharp_Delegates_and_Events
http://www.aspfree.com/c/a/C-Sharp/C-Sharp-Delegates-Explained/

Vous aimerez peut-être aussi