Académique Documents
Professionnel Documents
Culture Documents
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.
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 {
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);
}
class DelegateDemo1 {
static void Main(string[] args) {
// create the Greeting object
Greeting gr = new Greeting();
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.
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
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);
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
fl.Close();
}
}
}
Compile an test:
# csc SimpleEvent.cs
# SimpleEvent.exe
Process() begin
Process() end
# cat process.log
Process() begin
Process() end