Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

C# Basics: Delegates

DZone 's Guide to

C# Basics: Delegates

In this post, we take a look at what delegates in C# are and how to implement them in your C# code.

· Web Dev Zone ·
Free Resource

Delegates are one of the most used features of C#. They allow you to pass a function as a function pointer. It is kind of the same as a function pointer in C++.

Put simply, delegates are the same as a function pointer in C ++. They refer to another function.

Delegates in C#

As noted in Microsoft's official documentation:

"A delegate is a type that represents references wo methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance."

Before going deeper into the technical jargon about delegates, let us first create a delegate.

// declaring a delegate 
       public delegate int AddDelegate(int num1, int num2); // 1 
       static void Main(string[] args)
       {
           AddDelegate d1 = new AddDelegate(add); // 2 
           int result = d1(7, 2); // 3
           Console.WriteLine(result);
           Console.ReadKey(true);

       }
       public static int add(int a, int b) // function 
       {
           Console.WriteLine(" A Delegate Called me");
           int res;
           res = a + b;
           return res;

       }

Let's talk through the above code.

  1. Just before the main function in comment #1, we are declaring a delegate named AddDelegate. The signature of a delegate is very important, because a delegate can only refer to functions matching the same signature.
  2. We created a delegate with a return type set to integer and it takes two input integer parameters.
  3. In comment # 2, we are instantiating a delegate and passing the add function as a reference.
  4. In comment # 3 we're invoking the delegate.

Upon running the above code, you should get an output like the below image:

One important thing you need to keep in mind is that the signature of delegates must match with the signature of the function pointed by the delegate. Not only the signature, but also the return type should match.

Now, I hope you understand how a delegate can be created and used. Some features of delegates are as follows:

  • Delegates are similar to C++ function pointers:
    • But it is type safe.
    • Can encapsulate object instance.
    • Can encapsulate a method.
  • Delegates allow methods to pass as a parameter.
  • Delegates can be chained together.
  • Delegates can be used to define a callback method. For example, you can use it inside an event.
  • Delegates can compile anonymous method and lambda expressions.

You can encapsulate a lambda expression in a delegate as shown in following code listing:

AddDelegate d2 = (a, b) =>
        {
            Console.WriteLine(" delegate lambda expression ");
            int res1 = a + b + 10;
            return res1;
        };

        int result2 = d2(10, 20);
        Console.WriteLine(result2);

You will get an output as shown in the below image:

You can encapsulate more than one function in a delegate using a multicast delegate. You use multicast delegates to assign more than one object to one instance of an object. In other words, you can have more than one subscriber for the same delegate instance, and this is very useful in assigning multiple subscribers to an event. Consider this next code listing to understand an example of multicast delegates.

    class Program
    {
        public delegate void AddDelegate(int num1, int num2);
        static void Main(string[] args)
        {
            AddDelegate d1 = add;
            d1 += add100;
            d1 += (int a, int b) =>
            {
                int res = (a + b) * 1000;
                Console.WriteLine(res);
            };
            d1(10, 20);
            Console.ReadKey(true);

        }
        public static void add(int a, int b)
        {

            int res;
            res = a + b;
            Console.WriteLine(res);

        }
        public static void add100(int a, int b)
        {
            int res;
            res = a + b + 100;
            Console.WriteLine(res);
        }
    }
}

We have created a delegate then added multiple functions to encapsulate in the delegate instance reference. We are adding the add and add100 functions and a lambda expression to the same delegate instance reference.

Now when we call the delegate, these three references will be called and you will get the output shown in the below image. You can use + or - to add or remove an object reference from a multicast delegate.

Delegates have two more important concepts:

  • Covariance
  • Contravariance

From the beginning of this article, we have been saying that a delegate can refer to a method with the same return type and signature, however, variance eases this requirement. Covariance and contravariance provide flexibility for matching a delegate type with a method signature.

Covariance permits a method to have a return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.

To understand covariance, let's create two classes as shown in next code listing:

    public class Animal
    {
        protected int age;
        protected void displayAge(int age)
        {
            this.age = age;
            Console.WriteLine(this.age);
        }
    }

    public class Dog : Animal
    {
        public string color;
        public void displayColor()
        {
            displayAge(99);
            Console.WriteLine(this.color);
        }
    }
}

The Dog class inherits the Animal class. So using covariance, a delegate with type Animal can refer to the Dog type. We are creating a delegate with return type of Animal and assigning a reference of Dog type in the below code listing. This is possible due to the covariance feature of delegates.

public delegate Animal AnimalDelegate();

static void Main(string[] args)
{
    AnimalDelegate an = newDog;
    an();
    Console.ReadKey(true);


}

static Dog newDog()
{
    Dog d = new Dog();
    d.color = "white";
    d.displayColor();
    return d;
}

Like variance, contravariance allows us to work with methods that have parameters of a type that are base types of the delegate signature parameter type. Now I hope you understand delegates in C# and you can use this function in your projects whenever required. Besides the topics covered above, you may also want to check generic delegates.

Topics:
contravariance ,covariance ,delegates ,c# tutorial ,web dev

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}