Generic Delegates

Greetings to my blog readers!

I have previously covered delegates and events, but I’ve decided to go over the concept of delegates in C# once more. In addition, this time I will mention generic delegates.

So, let’s refresh – what is a delegate? A delegate is a type. It is a type that is designed to be compatible with methods. You may have come across someone comparing C# delegates with C++ function pointers. It is a fair functional comparison. That is, only a certain part of how C++ pointers behave and what they are used for is comparable with the C# delegates. As far as the internal representation – there is really almost nothing in common. The reason I bring up the function pointer is because I too think of delegates as being “like function pointers”. This makes it easier to understand their purpose and syntax. Let me reinforce this functional comparison with an example. Take the below declaration of a delegate:

public delegate T AnyMethod<T>(T input_param);

This declares a delegate that “can represent” any method that takes one parameter and returns one parameter. In other words, AnyMethod delegate is compatible with any method with a signature like that of this delegate. Since AnyMethod is generic, this gives us flexibility in what methods we can assign. Take a look at the below example code which was written to simply support this explanation. Given some appropriate type, AnyMethod can be assigned to a method that works with any parameter type.

using System;

namespace GenericDelegate
{
 
    public delegate T AnyMethod<T>(T input_param);
      

    class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            AnyMethod<int> im = IntMethod;          // this is the same as AnyMethod<int> im = new AnyMethod<int>(IntMethod); 
            AnyMethod<string> sm = StringMethod;    // this is the same as AnyMethod<string> sm = new AnyMethod<string>(StringMethod);
            
            im(i);
            sm(s);
        }

        static int IntMethod(int int_param)
        {
            Console.WriteLine("Inside IntMethod with "+int_param.ToString());
            return 0;
        }

        static string StringMethod(string string_param)
        {
            Console.WriteLine("Inside StringMethod with " + string_param);
            return "void";
        }
    }
}

AnyMethod delegate can work with generic methods as well. I can easily replace my two static methods with a single generic one.

class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            AnyMethod<int> im = TMethod;          // same as AnyMethod<int> im = new AnyMethod<int>(TMethod);  
            AnyMethod<string> sm = TMethod;       // same AnyMethod<string> sm = new AnyMethod<string>(TMethod); 
            
            im(i);
            sm(s);
        }

         static T TMethod<T> (T t_param)
         {
             Console.WriteLine("Inside TMethod with " + t_param.ToString());
             return t_param;
         }
    }

Remember I wrote that a delegate is a type. Note, I did not write that it is simply a method. Because it is not. Under the hood, for the delegate declaration on line 6, the compiler will be busy creating a real class (albeit a sealed one) that extends System.MultiCastDelegate class, has a constructor and three methods! I prefer not to bother with these details because they tend to confuse me. Instead, I will stick with the comfortable “types compatible with methods” definition. In fact, I like this definition so much that I will remove that delegate declaration entirely…And replace it with Func:

using System;

namespace GenericDelegate
{
    class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            Func<int, int> im = TMethod;             // Func<int,int> im = new Func<int,int>(TMethod);   
            Func<string, string> sm = TMethod;       //  Func<string,string> sm = new Func<string,string>(TMethod); 
            
            im(i);
            sm(s);
        }

         static T TMethod<T> (T t_param)
         {
             Console.WriteLine("Inside TMethod with " + t_param.ToString());
             return t_param;
         }
    }
}

Much better! If you have never come across Func before, take a look at the reference source. You will see that Func is just another delegate (with an upper bound on its input parameters), but its definition has been hidden away. Action and Func were introduced in .Net 3.5. I believe they were added to support lambda expressions. However, both are very handy in covering up the unnecessary complexity and reinforcing my earlier analogy.