• Delegates Now and Then

    Published by on February 28th, 2012 8:07 pm under C#, Delegates

    1 Comment

    A Delegate is a strongly-typed pointer to other method. In C#, all delegates are Multicast by default, which means that a single Delegate can point to many other methods.

    Their main purpose in the .NET world, is to provide Callback functionality and Asynchronous Event Handling. I won’t cover the latter in this article.

    Callback functionality, it is the ability execute methods within others, passing them as parameters. Delegates were designed to encapsulate methods to use them as callbacks.

    As I said earlier, Delegates allows us to execute more than one method with a single call. In order to make this work, all the methods pointed by the Delegate must have the exact same signature.

    Firstly, I will show how Delegates were used in the earliest versions of .NET Framework and how they evolved.

    Consider the following Example:

    public class Logger
        {
            public delegate void LogHandler(string message, string category);
    
            private LogHandler myDelegate = null;
    
            public void AddListener(LogHandler yourMethod)
            {
                this.myDelegate += yourMethod;
            }
    
            public void Log(string message, string category = "Information")
            {
                this.myDelegate(message, category);
            }
        }
    

    Here we have a “Server” class that creates the Delegate LogHandler, which takes two string parameters. So… this Delegate will be able to point other methods that have the same amount, and type of arguments. These methods will be often referred as Listeners.

    The AddListener method, takes as a parameter, what is called: a Callback method, of type LogHandler.
    Whenever AddListener method is invoked, we have to pass a LogHandler-compliant method that we want to register. Only the method’s name should we pass, not the arguments. As we are not calling it right now, we are only registering it.
    Finally, the Log method serves as an entry point, receiveng the parameters and then passing them to the Delegate which will then, execute each registered method.

    We are going to register two methods, one that will log in the Console, and the other is from another class that logs in a File:

    public class Program
        {
            public static void LogInConsole(string message, string category)
            {
                Console.WriteLine("{0} printed with method in the same class.", message);
            }
    
            public static void Run()
            {
                Console.WriteLine("Logger with Delegate Example");
                Console.WriteLine("=========================\n");
    
                Logger logger = new Logger();
                FileLogger fileLogger = new FileLogger();
    
                // Listeners
                logger.AddListener(LogInConsole);
                logger.AddListener(fileLogger.LogInFile);
    
                // act
                logger.Log("Begin:");
                logger.Log("End:");
    
                Console.WriteLine("press any key to continue...\n");
                Console.ReadKey();
            }
        }
    

    As seen, Delegates allow us to invoke methods with the same signature, from different classes, with a single call.

    Action<>, Func<> and Predicate<>

    Action Func and Predicate is the evolution of Delegates. Introduced on the latest versions of .NET Framework.
    Their power resides in improving readability of the code and avoiding an explicit declaration of a Delegate.
    If you understand one of them, you understand them all, because the difference between these three is the return type:

    Action <> returns void
    Func<> returns any type wanted
    Predicate<> returns a boolean

    Our previous example could be rewritten using one of this Delegates… which one? The answer is simple, the logging methods need to return: void. So Action<> it is:

    public class Logger
        {
            private Action<string, string> listeners;
    
            public void AddListener(Action<string, string> listener)
            {
                this.listeners += listener;
            }
    
            public void Log(string message, string category = "Information")
            {
                    this.listeners(message, category);
            }
        }
    

    Sources:

    • http://dissertation-topics-examples.info/ dissertation samples pdf

      Delegates were designed to encapsulate methods to use them as callbacks.