20

I understand that a declaration of a delegate is something like this:

public delegate int PerformCalculation(int x, int y);

However, there must be more going on. The purpose of the delegate is to provide a pointer to a method, and to do that you encapsulate the reference to the method in the delegate.

What kind of structure is this reference held in (internally in the delegate)? I also understand that you can encapsulate a reference to multiple methods in a delegate. Does this mean that there is an array in the delegate that holds these?

Also, what methods are defined in the delegate, etc. What is really happening when you declare a delegate with the terse:

public delegate int PerformCalculation(int x, int y);

?

EDIT: Some clarification. When you declare a delegate, the compiler automatically creates a sealed class that inherits from System.MulticastDelegate for you. You can see this if you look at your assembly with ildasm. This neat. Basically, with one statement, you are getting an entire new class generated for you at compile time, and it has all the functionality you need.

richard
  • 10,572
  • 20
  • 83
  • 144
  • 8
    You can check a lot of it with reflector. There are some parts missing because they require runtime-magic. But still you'll see most of the contents of single and multicast delegates. – CodesInChaos Jan 09 '11 at 21:27

2 Answers2

19

Internally it's a reference type, quite similar to a class. Transcribed it looks like this:

public /* delegate */ class PerformCalculation : MulticastDelegate {
    public PerformCalculation(object target, IntPtr method) {}
    public virtual void Invoke(int x, int y) {}
    public virtual IAsyncResult BeginInvoke(int x, int y, AsyncCallback callback, object state) {}
    public virtual void EndInvoke(IAsyncResult result) {}
}

I left the implementations of these members empty, they are actually mapped to code in the CLR. The compiler dynamically generates the method signatures, depending on the signature of the delegate declaration. Note the x and y arguments. The JIT compiler helps to get the constructor called, using the += or -= syntax, it knows the memory address of the delegate target method. The compiler automatically generated the target argument value, depending on whether the target method was static or not. The two arguments map to the (Multicast)Delegate.Target and Method properties. The actual base class instance can be either Delegate or MulticastDelegate, depending on how many targets were subscribed.

Lots of secret sauce going on here.

Hans Passant
  • 873,011
  • 131
  • 1,552
  • 2,371
  • Good stuff. Can you explain the overhead involved with a delegate invocation? In particular, vs. a virtual method call. – Ani Jan 09 '11 at 21:48
  • 2
    Hard to tell, the debugger is *very* cranky about debugging this code. There's certainly overhead, it isn't a simple virtual method call. Afaict, it calls the jitted Invoke() method which then make an indirect call to a thunk that massages the stack frame and calls the correct invoker, depending on whether its a Delegate or a MulticastDelegate. The latter needs to iterate the invocation list. It's pretty cheap when I profiled it (nanoseconds) but certainly not as cheap as just a method call. – Hans Passant Jan 09 '11 at 22:38
  • Thanks Hans. The internal stuff is very helpful and interesting. – richard Jan 09 '11 at 22:39
  • I saw some time ago some benchmarks about this. I can't find it again, however, but take it or not, the overhead was surprisingly small. – Eilistraee Mar 06 '11 at 11:05
  • This should really mention the `Target` and `Method` properties; they're crucially important to understanding what a "delegate instance" is made of, IMO. – Roman Starkov Jan 04 '12 at 17:59
  • Out of curiosity, what are the advantages of having `Delegate` itself include multicast functionality, rather than having `Delegate.Combine` produce a delegate whose `Target` is an array of delegates, and whose `Method` is a method that invokes all the items in the passed-in array? – supercat May 13 '13 at 16:28
9

All delegates inherit from the System.Delegate type which hold a Target and Method. More precisely they inherit from System.MultiCastDelegate which inherits from System.Delegate.

Darin Dimitrov
  • 960,118
  • 257
  • 3,196
  • 2,876
  • 2
    Thanks! That is pretty much exactly what I was looking for. I feel dumb for not finding that myself. I can't seem to find how the delegate holds multiple references. It has a target and a method, but these don't look like collections . . . What am I missing here? – richard Jan 09 '11 at 21:36
  • @Richard DesLonde, you are missing [System.MulticastDelegate](http://msdn.microsoft.com/en-us/library/system.multicastdelegate.aspx) which is what all delegates inherit from. I've updated my answer for more clarity. – Darin Dimitrov Jan 09 '11 at 21:38
  • 2
    They don't implement - they inherit. – Ani Jan 09 '11 at 21:42
  • Thanks, so internally, the multicast delegate has a linked list of delegates? @Ani: Thanks for the corrections. That makes sense. – richard Jan 09 '11 at 22:38
  • 1
    @richard They may have used a linked list in the first version of the framework, but current versions use a simple immutable array of delegates which is thrown away every time you add or remove delegates to the multicast delegate. – Ark-kun Jul 29 '14 at 20:37