C# Delegates Tutorial

In this tutorial we learn about delegates in C#, which are similar to function pointers in C or C++.

We cover how to declare a delegate, delegate safety, how to use a delegate as a method parameter and how to point a delegate to multiple methods with multicasting.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is a delegate

A delegate in C# is similar to function pointers in C or C++. A delegate holds a reference (pointer) to a method, which can be changed at runtime.

How to declare a single delegate

A delegate doesn’t know, or care, about the class of the object it references. It will accept any method if that method’s parameter list match the ones in the delegate.

Delegates allow us to specify what the method we want to call looks like, without having to specify the exact method to call.

Syntax:
 access_modifier delegate return_type delegate_name(parameter_list);
Syntax Example:
 public delegate void Logger(string logMessage);
Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            Del del = new Del(Message);

            del();

            Console.ReadLine();
        }

        public delegate void Del();

        public static void Message()
        {
            Console.WriteLine("I was called by a delegate");
        }
    }
}

Let’s break it down step by step:

  1. We create a void delegate called Del with no parameters. Any method that is also void and has no parameters, can potentially be called by this delegate.
  2. We create a void method called Message with no parameters. This method’s signature is the same as the delegate’s signature so the delegate may call this method.
  3. In the Main() method we create a new object instance of the delegate and we pass the method we want to associate with it as a parameter.
  4. Then we call the object instance as a method at which point it will call the Message method.

Now let’s see what it would look like if we wanted parameters:

Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            Del del = new Del(Message);

            del("I was called by a delegate");

            Console.ReadLine();
        }

        public delegate void Del(string strParam);

        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }
    }
}

Let’s break it down again:

  1. We added a string parameter to the delegate. Any methods that want to use the delegate must hav a string parameter as well.
  2. We added a string parameter to the Message method, that will be printed to the console.
  3. In the Main() method we passed a string into the delegate call.

Delegate safety

We should add a check to see if the delegate is even pointing to a method. If it’s not pointing to a method it will return null, so we can do a simple not null check.

Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            Del del = new Del(Message);

        if (del != null)
            del("I was called by a delegate");
        else
            Console.WriteLine("Null pointer.");

            Console.ReadLine();
        }

        public delegate void Del(string strParam);

        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }
    }
}

In the example above, we added an if-else check to see if the reference is null, with an error message if it is.

A delegate as method parameter

We can use a delegate as a parameter if we specify the delegate as a parameter type in a method.

Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            DelAsParam(new Del(Message));
        }

        public delegate void Del(string strParam);

        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }

        public static void DelAsParam(Del delParam)
        {
            if (delParam != null)
                delParam("I was called by a delegate");
            else
                Console.WriteLine("Null pointer.");

            Console.ReadLine();
        }
    }
}

In the example above, we took everything out of the Main() method and placed it inside a method called DelAsParam with a parameter of the Del type.

In the Main() method we called the new DelAsParam method. As its parameter we create a new instance of the delegate with the Message method associated to it.

How to point a delegate to multiple methods with multicasting

Delegates are multicast, which means they can point to more than one method at a time.

A multicast delegate maintains a list of methods that will be called when the delegate is invoked.

We add methods to the list by using the += operator. These methods will be invoked sequentially, that’s to say, one after the other.

Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            Del del = new Del(Message1);
            // add Message2
            del += Message2;

            del();

            Console.ReadLine();
        }

        public delegate void Del();

        public static void Message1()
        {
            Console.WriteLine("I was called first");
        }

        public static void Message2()
        {
            Console.WriteLine("I was called second");
        }
    }
}

In the example above we added another method called Message2. In the Main method we add the Message2 method to the list that the delegate must invoke by using the += operator on the delegate object.

We can remove methods from the multicast list with the -= operator.

Example:
using System;

namespace Delegates
{
    class Program
    {
        static void Main()
        {
            Del del = new Del(Message1);
            // add Message2
            del += Message2;

            // remove Message1
            del -= Message1;
            del();

            Console.ReadLine();
        }

        public delegate void Del();

        public static void Message1()
        {
            Console.WriteLine("I was called first");
        }

        public static void Message2()
        {
            Console.WriteLine("I was called second");
        }
    }
}

In the example above, we add the Message2 to the multicast list with the += operator. Then, we remove Message1 from the multicast list with the -= operator and call the delegate.

The text from Message1 was removed from the list so the method was never called and the text wasn’t printed to the console.

Summary: Points to remember

  • A delegate holds a reference, or pointer, to a method that can be changed at runtime, similar to function pointers in C.
  • A delegate will accept any method if the method’s parameters match those in the delegate, and allows us to specify what the method call would look like.
  • It’s good practice to perform a null check on a delegate to see if it’s pointing to something.
  • We can specify a delegate as a parameter type in a method to use it as a parameter.
  • A delegate can point to more than one method at a time. We add a method to the multicast list with the += operator.