This is not so easy to explain at once. And yes, most of the articles I ever saw are unsatisfactory.
First, you should not mix up
delegate types and
delegate instances. The relationship between them is very different from relationships between other user-defined types and instances.
A delegate type defined a signature of the method with could serve as a handler method which could be added to an
invocation list of a delegate instance, such as
delegate uint StringHandler(string value, int index);
Now, let's define a delegate index:
StringHandler stringHandler;
Now, the question is: what is the type of
stringHandler
? Is it
StringHandler
?
It is — surprise! — listen carefully, pay full attention;
it is not!
It' is easy to find out, but you can do it only if you add some handler to the instance; before you do, this object is always null. OK, let's do it:
stringHandler = (value, index) => {
if (value.IsNullOrEmpty)
return 0;
else
return value.Length - index;
};
For C# v.2, you cannot use lambda syntax or type inference, so the syntax would be a bit longer:
stringHandler = delegate(string value, int index) { }
Now, you can get a run-time type of the instance:
System.Type delegateInstanceType = stringHandler.GetType();
Examine this type; and you will see that this is a type which has nothing to do with
StringHandler
. You will find out that this is a… class, derived from
System.Delegate
. Please see:
http://msdn.microsoft.com/en-us/library/system.delegate.aspx[
^].
It has many members, but you will find that it has the member representing its
invocation list:
GetInvocationList
:
http://msdn.microsoft.com/en-us/library/system.delegate.getinvocationlist.aspx[
^].
The members of the list are… also delegate instances. Recursion in action? Not exactly. The list has interesting functionality: it is
flat. Its member cannot be null, and its member is never multicast.
So,
what makes a delegate instance callable? It resembles a method, because it can be invoked via the method
Invoke
, which calls all the elements of the invocation list. OK, but what makes those methods callable. Essentially, they point to two things: first, this is
the address of a method to be called. Second, if the method is the instance (non-static method), a delegate instance should
store the reference to the instance of some type to be passes as "this" to the instance method. This is exactly what happens. The handler added with '+=' can come in the form
instance.Method
or
type.Method
. In first case, the reference to the instance is passed to the invocation list; the second case is the case of static method, the reference to the instance is not used. When a delegate invocation happens, the stored reference to the instance is passed to a called method as the implicit "this" parameter.
Is the invocation list actually grow when another handler is added. Surprise again:
it is not (!). Delegate instances are
immutable. When '+=' operator is applied, a brand new delegate instance is created, with longer invocation list. As a result, the variable representing the delegate instance is loosing its
referential identity. One reason for such architecture is the improvement in multithreading.
This topic is explained in the section 4.1 of article
Dynamic Method Dispatcher[
^].
For additional; explanation on static and instance methods, please see my past answer:
What makes static methods accessible?[
^].
I guess next question would be on event… To be intrigues a bit, please see this question and my answer:
Since we have multicast delegates, why do we need events?[
^].
Basically, delegates is the mechanism to introduce
first-class functions in .NET. Please see:
http://en.wikipedia.org/wiki/First-class_function[
^].
In particular, delegate instanced can be passed as parameters to other methods which are used to abstract out algorithms. This is one of them major features of
functional programming. Please see:
http://en.wikipedia.org/wiki/Functional_programming[
^].
—SA