Time has come to master delegates

The objective of this post is to show, in a hands-on way, the basic use of ordinary delegates and System.Func delegates with normal functions, anonymous functions and lambda expressions (which are actually anonymous functions as well...). You can deep into the topic and obtain much more information, samples and theory at here.

To begin with, below it is shown how to define classic delegates:

 class DelegateClassic
    {
        private delegate int AddHandler(int a, int b);
        private delegate string ConcatHandler(string a, string b);
        private delegate int GetRandomValueHandler(int a, int b);

        private DelegateClassic(){}

        public static void Run(int a, int b)
        {
            Console.WriteLine("Running Classic Delegate Demo...");

            AddHandler addHandler = new AddHandler(GlobalHelper.Add);
            addHandler.Invoke(a, b);

            GetRandomValueHandler getRandomValueHandler = new GetRandomValueHandler(GlobalHelper.GetRandomValue);
            getRandomValueHandler.Invoke(a, b);

            ConcatHandler concatHandler = new ConcatHandler(GlobalHelper.Concat);
            concatHandler.Invoke(a.ToString(), b.ToString());

        }
    }

First, delegates are declared. Then, delegates are instantiated and handlers are passed in to execute code when delegates are invoked. It's easy to understand. This case, all handlers methods are within a GlobalHelper class.

The next sample executes the same handler methods, but this time I'm not using "classic" definition of delegates but a System.Func delegate. System.Func is a generic delegate included in the System namespace. It can have zero or more input parameters and one output parameter. The last one is considered, by convention, the output parameter and it always must exists. The code is totally equivalent to the first sample but it's much simpler and more straighforward because you don't need to declare each delegate.

 class DelegateFunc
    {
        private DelegateFunc() {}

        public static void Run(int a, int b)
        {
            Console.WriteLine("Running System.Func Delegate Demo...");

            Func func1 = new Func(GlobalHelper.Add);
            func1.Invoke(a, b);

            Func func2 = new Func(GlobalHelper.GetRandomValue);
            func2.Invoke(a, b);

            Func func3 = new Func(GlobalHelper.Concat);
            func3.Invoke(a.ToString(), b.ToString());

        }
    }

Perhaps, you don't need to declare handler methods in a separated way. You just need to execute some lines of code directly without using named functions. If so, you have at least two more options. You may employ anonymous functions or lambda expressions. I like much more the latter but it's up to you to choose one of them.

Here is the sample with System.Func delegate and anonymous functions:

 class DelegateFuncWithAnonymous
    {
        private DelegateFuncWithAnonymous(){}

        public static void Run(int a, int b)
        {
            Console.WriteLine("Running System.Func Delegate Demo with Anonymous functions...");

            Func func1 = delegate(int x, int y)
            {
                Console.WriteLine("Add {1} to {0} => {2}", x, y, x + y);
                return x + y;
            };
            func1.Invoke(a, b);

            Func func2 = delegate(int x, int y)
            {
                int res = new Random().Next(x <= y ? x : y, x <= y ? y : x);
                Console.WriteLine("Get Random Value between {0} and {1} => {2}", x, y, res);
                return res;
            };
            func2.Invoke(a, b);

            Func func3 = delegate(string x, string y)
            {
                string res = x + y;
                Console.WriteLine("Concat {0} and {1} => {2}", x, y, res);
                return res;
            };
            func3.Invoke(a.ToString(), b.ToString());
        }
    }

And here is the same example but making use of lambda expressions:

 class DelegateFuncWithLambdas
    {
        private DelegateFuncWithLambdas(){}

        public static void Run(int a, int b)
        {
            Console.WriteLine("Running System.Func Delegate Demo with Lambda expressions...");

            Func func1 = (x, y) =>
            {
                Console.WriteLine("Add {1} to {0} => {2}", x, y, x + y);
                return x + y;
            };
            func1.Invoke(a, b);

            Func func2 = (x, y) =>
            {
                int res = new Random().Next(x <= y ? x : y, x <= y ? y : x);
                Console.WriteLine("Get Random Value between {0} and {1} => {2}", x, y, res);
                return res;
            };
            func2.Invoke(a, b);

            Func func3 = (x, y) =>
            {
                string res = x + y;
                Console.WriteLine("Concat {0} and {1} => {2}", x, y, res);
                return res;
            };
            func3.Invoke(a.ToString(), b.ToString());
        }
    }

So far, all the examples execute the same code but in different ways. You can see how we have gone from using classic delegates until getting to System.Func delegate with lambda expressions.

To summarize, System.Func is a built-in delegate type that accepts different types of input parameters (from 0 to 16) and one mandatory output parameter. Besides, System.Func delegate type can be used with anonymous methods or lambda expressions. You should get used to these types of system delegates. Sooner or later, you'll see them a lot.

Here is the rest of my code:

Main Program:

 class Program
    {
        static void Main(string[] args)
        {
            try
            {
                /* Using delegates */
                Console.WriteLine(string.Format("{0} (x): ", RGlobal.EnterNumber));
                int x = int.Parse(Console.ReadLine());

                Console.WriteLine(string.Format("{0} (y): ", RGlobal.EnterNumber));
                int y = int.Parse(Console.ReadLine());
                
                /* Basic use of delegate */
                Console.WriteLine(System.Environment.NewLine);
                DelegateClassic.Run(x, y);
                Console.WriteLine(RGlobal.PressAnyKeyToContinue);
                Console.ReadLine();

                /* Syste.Func delegate */
                Console.WriteLine(System.Environment.NewLine);
                DelegateFunc.Run(x, y);
                Console.WriteLine(RGlobal.PressAnyKeyToContinue);
                Console.ReadLine();

                /* Syste.Func delegate with Anonymous functions */
                Console.WriteLine(System.Environment.NewLine);
                DelegateFuncWithAnonymous.Run(x, y);
                Console.WriteLine(RGlobal.PressAnyKeyToContinue);
                Console.ReadLine();

                /* Syste.Func delegate with Lambda expressions */
                Console.WriteLine(System.Environment.NewLine);
                DelegateFuncWithLambdas.Run(x, y);
                Console.WriteLine(RGlobal.PressAnyKeyToContinue);
                Console.ReadLine();
            }
            catch
            {
                Console.WriteLine(RGlobal.UnexpectedError);
                Console.WriteLine(RGlobal.PressAnyKeyToContinue);
                Console.ReadLine();
            }
 
        }

GlobalHelper class:

public class GlobalHelper
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("Add {1} to {0} => {2}", a, b, a + b);
            return a + b;
        }

        public static string Concat(string a, string b)
        {
            string res = a.ToString() + b.ToString();
            Console.WriteLine("Concat {0} and {1} => {2}", a, b, res);
            return res;
        }

        public static int GetRandomValue(int a, int b)
        {
            int res = new Random().Next(a <= b ? a : b, a <= b ? b : a);
            Console.WriteLine("Get Random Value between {0} and {1} => {2}", a, b, res);
            return res;
        }
    }

It's important to note that I'm making use of a RGlobal.resx file to store and reference "strings". It's a good practice in order to provide support for multilanguage applications.

Add comment