This article assumes basic programming experience with delegates.
Delegates are a .NET framework feature that allows for type-safe function pointers. Actually, they are a bit more than function pointers, because they are object oriented, as their second name -bound method references- expresses. It means that the method pointer stored in the delegate may actually be bound to an instance of a specific object: when the delegate is invoked, the method is called on that target object. In the code of that method, you can notice that the this variable refers to that same target object.
But as we'll also see, delegates aren't always bound to an object. They support storing a static method pointer, which isn't bound to any specific instance of the class, like classic C-style function pointers.
In this article, we'll dig into delegates, their specificities, some of their inner workings and the role of the compiler in their implementation.
Note: We won't consider the singlecast vs. multicast aspect of delegates.
Here is a little sample of code using delegates:
static void Main(string args)
Class1 logger = new Class1();
// Instanciate with static binding
logger.stringHandler = new StringLogging(Class1.StringOutput);
// Instanciate with instance binding
logger.stringHandler = new StringLogging(logger.StringDrop);
// Outputs strings to the console
public static void StringOutput(string msg)
// Drops strings into a blackhole
public void StringDrop(string msg)
Delegates vs. Java Listeners
Delegates are a pretty interesting construct, although somewhat "magical". They allow easy binding of event handlers and other callback mechanisms, without the use of a separate class implementing a specific listener interface (which is the Java approach for callbacks).
This page details the comparison between delegates and the classic Command design pattern.
Basically, instead passing a callback function via an object of known interface that both communicating parties agree on, delegates only need the two parties to use the same function signature. This function signature contract is set during the declaration of a delegate.
For example, in the public delegate void MouseClickDelegate(int buttonClicked) delegate declaration, the interface for the callback function is void foo(int).
This delegate declaration is actually transformed by the compiler into a class MouseClickDelegate that can only be created by binding it to a void foo(int) method and only be invoked following that same interface void Invoke(int). Both of these constraints are verified by the compiler at compile time.
You can run ildasm on an assembly containing a delegate definition, and check out the generated delegate class and its interface (notice the Invoke method). You can also see that this class inherits from System.MulticastDelegate and therefore indirectly from System.Delegate (we'll look at this some more further down).
So, delegates are actually implemented using objects, albeit special ones. This is nicely introduced on this "Understanding the Nuances of Delegates in C#" page at O'Reilly Network. In particular, delegates are declared in a way similar to other objects (inside a namespace or inside another object) and can be instantiated and passed around like other typed variables.
Unfortunately, reading this article only left me with more curiousity about delegates and their implementations. That's because the article doesn't explain how delegate classes are generated by the compiler (see example above), but also because it doesn't say how those method pointers are bound to the object holding the method.
Trying to figure out how delegates where not just function pointers, but actual method calls on an object, I dug up the System.Delegate class reference at MSDN.
The System.Delegate class has two interesting properties: Target (of type object) and Method (of type MethodInfo). Target references the object that the method is actually bound to. So, each delegate object is really a wrapper around a method and an object to be operated on when the method is called.
In the previous MouseClickDelegate delegate example, the dis-assembling of the assembly reveals the generated MouseClickDelegate class, with the detail of its attributes and methods. One of these methods is the following constructor (which is the same for all delegate objects): .ctor : void(object, native int).
This means that when you instanciate this class with new MouseClickDelegate(this.MyMouseClickListeningMethod), the compiler will actually replace this instanciation with a new MouseClickDelegate(this, ...). This is how the the delegate gets bound to the object.
When the delegate is instanciated with a static method, the object parameter in the constructor is null and the created delegate object has a null Target.
The assembly for such a static method delegates shows:
So we can see that in such an instantiation, there is no instance object (it's null), as the method is static.
We have seen that in the System.Delegate class, the Target references the object to which the method pointer is bound, but about the Method?
The Method property is a MethodInfo (part of the System.Reflection namespace). Also MSDN describes this property with: "Gets the method represented by the delegate". Does that mean every delegate invocation goes through Reflection?!
Although I don't have much knowledge on the Reflection component of the framework, I would expect such an implementation to have performance problems.
Looking at the dis-assembled MSIL code, we see that the invocation of the delegate looks like:
The compiler recognized the "function call" on the delegate and replaced it with a call to the delegates Invoke compiler-generated method. The compiler also blocks any explicit call Invoke from your code, by issuing an error: "Invoke cannot be called directly on a delegate".
Unfortunately, this doesn't reveal whether the Invoke method relies on reflection to implement the invocation or if it relies on a native CLR method call mechanism.
The MSIL code for the delegate's Invoke method doesn't bring any information either:
The "runtime managed" seems to indicate that the runtime is responsible for the implementation of this method. I haven't found how this works yet.
The only clues that I have gathered on the invocation mechanism are the signature of the delegate constructor (what is the second parameter in .ctor void(object, native int) ?) and a rather detailled explanation of the role of the compiler in the delegates' implementation in this delegates article in MSDNmag.
It appears that the second argument of the delegate constructor is a reference to the method, via "a special Int32 value (obtained from a MethodDef or MethodRef metadata token) that identifies the method is passed for the methodPtr parameter".
Unluckily, MethodDef and MethodRef don't seem to be well publicly documented as they don't bring much information on MSDN (they are mentioned hereon MSDN) or Google...
Update: I know understand what these token mean. For more info read my series on Runtime IL modification.
I am going to investigate the mono:: compiler source code to try and find the missing details about the implementation of the Invoke method and the meaning of the native int that references the method. But we have seen that the compiler plays a major role in making the delegates work.
Encountering a delegate declaration, the compiler actually generates a class with an Invoke method that matches the delegate declaration's signature.
Then, when a delegate instanciation is compiled, it gets verified (type-wise) and replaced by a call to the delegate constructor passing in an object and a method reference (via an int).
And last, the compiler recognizes delegates invocations and compiles them as call on the Invoke method.
Microsoft on delegates and comparison with Inner classes, is a response to Sun's criticism of delegates.
Compiler-supplied delegate methods at MSDN: Invoke, BeginInvoke and EndInvoke.Posted by Julien on April 18, 2003. Permalink