Delegates in .NET: how are they constructed?

1.8k views Asked by At

While inspecting delegates in C# and .NET in general, I noticed some interesting facts:

Creating a delegate in C# creates a class derived from MulticastDelegate with a constructor:

.method public hidebysig specialname rtspecialname instance 
   void .ctor(object 'object', native int 'method') runtime managed { }

Meaning that it expects the instance and a pointer to the method. Yet the syntax of constructing a delegate in C# suggests that it has a constructor

new MyDelegate(int () target)

where I can recognise int () as a function instance (int *target() would be a function pointer in C++). So obviously the C# compiler picks out the correct method from the method group defined by the function name and constructs the delegate. So the first question would be, where does the C# compiler (or Visual Studio, to be precise) pick this constructor signature from ? I did not notice any special attributes or something that would make a distinction. Is this some sort of compiler/visualstudio magic ? If not, is the T (args) target construction valid in C# ? I did not manage to get anything with it to compile, e.g.:

int () target = MyMethod;

is invalid, so is doing anything with MyMetod, e.g. calling .ToString() on it (well this does make some sense, since that is technically a method group, but I imagine it should be possible to explicitly pick out a method by casting, e.g. (int())MyFunction. So is all of this purely compiler magic ? Looking at the construction through reflector reveals yet another syntax:

Func CS$1$0000 = new Func(null, (IntPtr) Foo);

This is consistent with the disassembled constructor signature, yet this does not compile!

One final interesting note is that the classes Delegate and MulticastDelegate have yet another sets of constructors:

.method family hidebysig specialname rtspecialname instance void .ctor(class System.Type target, string 'method') cil managed

Where does the transition from an instance and method pointer to a type and a string method name occur ? Can this be explained by the runtime managed keywords in the custom delegate constructor signature, i.e. does the runtime do it's job here ?

EDIT: ok, so I guess I should reformulate what I wanted to say by this question. Basically I'm suggesting that there's not only C# compiler / CLR magic involved in delegate construction, but also some Visual Studio magic, since Intellisense flips out some new syntax when suggesting the constructor arguments and even hides one of them (e.g. Reflector does not use this syntax and construtor, for that matter).

I was wondering whether this assertion is true, and whether the function instance syntax has some deeper meaning in C# or is it just some constant format implemented by the Visual Studio magic part for clarity (which makes sense, since it looks like invalid C#) ? In short, if I was implementing Intellisense, should I do some magic for delegates or could I construct the suggestion by some clever mechanism ?

FINAL EDIT: so, the popular consensus is that that's indeed VS magic. Seeing other examples (see Marc Gravell's comment) of such VS behavior convinces me that that is the case.

3

There are 3 answers

6
Marc Gravell On BEST ANSWER

The first argument is resolved from the object reference (or null for static methods); no magic there.

Re the second argument, however - it is an unmanaged pointer (native int); in short, there is no alternative direct C# syntax that can use this constructor - it uses a specific IL instruction (ldftn) to resolve the function from metadata. However, you can use Delegate.CreateDelegate to create delegates via reflection. You can also use IL emit (DynamicMethod etc), but it isn't fun.

2
Rory On

This is just a guess, so don't shoot me if I'm wrong, but I think Intellisense is getting the signature for the target method from the Invoke method defined on the delegate. Reflector clearly shows that the Invoke method on System.Action<T> is:

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void Invoke(T obj);

Which is the same as the signature suggestion offered by Intellisense. The magic is Intellisense, when spotting a delegate type, looks at the Invoke method and suggests a constructor that takes a target that matches it.

4
Zach Johnson On

You first define the delegate (this is how Visual Studio knows the signature of the target method):

delegate void MyDelegate();

Then you construct delegate instances like this:

MyDelegate method = new MyDelegate({method name});

// If this was the method you wanted to invoke:
void MethodToInvoke()
{
    // do something
}

MyDelegate method = new MyDelegate(MethodToInvoke);

C# automatically picks the method matching the signature of the delegate.

Edit: When Visual Studio's Intellisense shows you the int () target suggestion, it is showing you the signature of C# methods you can use. The C# compiler translates the C# representation to IL. The IL implementation will look different, because IL is not a C style language, and the C# compiler provides syntactic sugar to abstract away the implementation details.