What is a .NET generic delegate?

Posted: (EET/GMT+2)

 

If you are using C# 3.0 already, you've no doubt also trying out lambda expressions. However, lambda expressions are often seen in LINQ statements, but they are also useful elsewhere.

To help with this, the System namespace (in the System.Core assembly) defines several so-called generic delegates, such as one called "Func". These generic delegates let you easily define delegates with common signatures, such as a delegate taking one parameter and returning a value. Since generics are supported, you can also specify those datatypes that you want.

Here's an example. Say, you wanted to quickly implement a method to convert an integer value to a string. Furthermore, you might need to pass this method as a parameter (delegate) to another method. To do this, you could use the Func<T, TResult> generic delegate, and define the method like this using a lambda expression:

Func convert = num => num.ToString();

Now, you have defined the method "convert", which takes in an int and returns a string. In case you are interested, here's the CIL (Common Intermediate Language) of the above C# code:

.method private hidebysig instance void button1_Click(
  object sender, class [mscorlib]System.EventArgs e)
  cil managed
{
.maxstack  3
.locals init ([0] class [System.Core]
  System.Func`2 convert,
         [1] int32 'value',
         [2] string str)
IL_0000:  nop
IL_0001:  ldsfld     class [System.Core]System.
  Func`2 WinFormsTestApp.Form2::
  'CS$<>9__CachedAnonymousMethodDelegate4'
IL_0006:  brtrue.s   IL_001b
IL_0008:  ldnull
IL_0009:  ldftn      string WinFormsTestApp.Form2::
  'b__3'(int32)
IL_000f:  newobj     instance void class [System.Core]
  System.Func`2::.ctor(object,
  native int)
IL_0014:  stsfld     class [System.Core]
  System.Func`2 WinFormsTestApp.Form2::
  'CS$<>9__CachedAnonymousMethodDelegate4'
IL_0019:  br.s       IL_001b
IL_001b:  ldsfld     class [System.Core]
  System.Func`2 WinFormsTestApp.Form2::
  'CS$<>9__CachedAnonymousMethodDelegate4'
IL_0020:  stloc.0
IL_0021:  ldc.i4.s   123
IL_0023:  stloc.1
IL_0024:  ldloc.0
IL_0025:  ldloc.1
IL_0026:  callvirt   instance !1 class [System.Core]
  System.Func`2::Invoke(!0)
IL_002b:  stloc.2
IL_002c:  ldloc.2
IL_002d:  call       valuetype [System.Windows.Forms]
  System.Windows.Forms.DialogResult [System.Windows.Forms]
  System.Windows.Forms.MessageBox::Show(string)
IL_0032:  pop
IL_0033:  ret    
}

-------

.field private static class [System.Core]
  System.Func`2
  'CS$<>9__CachedAnonymousMethodDelegate4'
.custom instance void [mscorlib]
  System.Runtime.CompilerServices.
  CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 

-------

.method private hidebysig static string
  'b__3'(int32 num) cil managed
{
  .custom instance void [mscorlib]System.Runtime.
    CompilerServices.CompilerGeneratedAttribute::
    .ctor() = ( 01 00 00 00 ) 
  // Code size       12 (0xc)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  ldarga.s   num
  IL_0002:  call       instance string [mscorlib]
    System.Int32::ToString()
  IL_0007:  stloc.0
  IL_0008:  br.s       IL_000a
  IL_000a:  ldloc.0
  IL_000b:  ret
}

Being able to use delegates properly is a great aid in many C#/.NET programming situations. Combining lambda expressions and generic delegates to the equation gives you a powerful set of tools.