Delegate and MulticastDelegate

Ever wonder what the difference between Delegate and MulticastDelegate? You might think it has something to do with Multicasting right? Wrong.

We abandoned the distinction between Delegate and MulticastDelegate towards the end of V1. At that time, it would have been a massive change to merge the two classes so we didn’t do so. You should pretend that they are merged and that only MulticastDelegate exists. For those performance oriented among you, please note that if you have a MulticastDelegate that hasn’t been combined with any other delegates the CLR can optimizes for this case during dispatch and in how the JIT compiles the callsite…. (Although I don’t think that optimization made it into V1.1, it will be coming in a future release).

In fact, C#\VB, etc will not even allow you to drive from Delegate directly. All delegates extend MulticastDelegate. You can tell this by ILDASMing your code:

Say you have the C# code:

public delegate int GetThing();

That produces the IL:

.class public auto ansi sealed GetThing

       extends [mscorlib]System.MulticastDelegate

{

  .method public hidebysig specialname rtspecialname

          instance void .ctor(object 'object',

                               native int 'method') runtime managed

  {

  } // end of method GetThing::.ctor

  .method public hidebysig virtual instance int32

          Invoke() runtime managed

  {

  } // end of method GetThing::Invoke

  .method public hidebysig newslot virtual

          instance class [mscorlib]System.IAsyncResult

          BeginInvoke(class [mscorlib]System.AsyncCallback callback,

                      object 'object') runtime managed

  {

  } // end of method GetThing::BeginInvoke

  .method public hidebysig newslot virtual

          instance int32 EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed

  {

  } // end of method GetThing::EndInvoke

} // end of class GetThing

The method bodies for Invoke, BeginInvoke, EndInvoke and the constructor are marked as ‘runtime’ in the IL. In the CLR, we refer to this case as ‘EEImpl’ or implemented by the ExecutionEngine. There is no IL for these methods and the CLR supplies an appropriate implementation using its own magic (isn’t great to be the runtime).

Oh, and just to be sure we keep the compilers honest… go try to hack the IL to make the “GetThing” extend from System.Delegate instead… You will run into this nice little check in the loader:

           else if (pParentClass->GetMethodTable()->IsSingleDelegateExact()) {

                    // We don't want MultiCastDelegate class itself to return true for IsSingleCastDelegate

                    // rather than do a name match, we look for the fact that it is not sealed

                if (pModule->GetAssembly() != SystemDomain::SystemAssembly()) {

                    BAD_FORMAT_ASSERT(!"Inheriting directly form Delegate class illegal");

                    m_pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT);

                }

You can find it in the Rotor sources in Vm\clsload.cpp:1887