Delegates and Events
People often find it difficult to see the difference between events and delegates. C# doesn't help matters by allowing you to declare field-like eventswhich are automatically backed by a delegate variable of the same name. This article aims to clarify the matter for you. Another source of confusion is the overloading of the term "delegate". Sometimes it is used to mean a delegate type, and at other times it can be used to mean an instance of a delegate type. I'll use "delegate type" and "delegate instance" to distinguish between them, and "delegate" when talking about the whole topic in a general sense.
Delegate types
In some ways, you can think of a delegate type as being a bit like an interface with a single method. It specifies the signature of a method, and when you have a delegate instance, you can make a call to it as if it were a method with the same signature. Delegates provide other features, but the ability to make calls with a particular signature is the reason for the existence of the delegate concept. Delegates hold a reference to a method, and (for instance methods) a reference to the target object the method should be called on.
Delegates types are declared with the delegate
keyword. They can appear either on their own or nested within a class, as shown below.
namespace DelegateArticle
{
public delegate string FirstDelegate (int x);
public class Sample
{
public delegate void SecondDelegate (char a, char b);
}
}
This code declares two delegate types. The first is DelegateArticle.FirstDelegate
which has a single parameter of type int
and returns a string
. The second is DelegateArticle.Sample.SecondDelegate
which has two char
parameters, and doesn't return anything (because the return type is specified as void
).
Note that the delegate
keyword doesn't always mean that a delegate type is being declared. The same keyword is used when creating instances of the delegate type using anonymous methods.
The types declared here derive from System.MulticastDelegate
, which in turn derives from System.Delegate
. In practice, you'll only see delegate types deriving from MulticastDelegate
. The difference between Delegate
and MulticastDelegate
is largely historical; in betas of .NET 1.0 the difference was significant (and annoying) - Microsoft considered merging the two types together, but decided it was too late in the release cycle to make such a major change. You can pretty much pretend that they're only one type.
Any delegate type you create has the members inherited from its parent types, one constructor with parameters of object
and IntPtr
and three extra methods: Invoke
, BeginInvoke
and EndInvoke
. We'll come back to the constructor in a minute. The methods can't be inherited from anything, because the signatures vary according to the signature the delegate is declared with. Using the sample code above, the first delegate has the following methods:
public string Invoke (int x);
public System.IAsyncResult BeginInvoke(int x, System.AsyncCallback callback, object state);
public string EndInvoke(IAsyncResult result);
As you can see, the return type of
Invoke
and
EndInvoke
matches that of the declaration signature, as are the parameters of
Invoke
and the first parameters of
BeginInvoke
. We'll see the purpose of
Invoke
in the next section, and cover
BeginInvoke
and
EndInvoke
in the
section on advanced usage. It's a bit premature to talk about calling methods when we don't know how to create an instance, however. We'll cover that (and more) in the next section.