Explore Delegates In C#

Introduction

This article of the series, “Diving into OOP,” will explain all about delegates in C#. This article focuses more on practical implementations and less on theory, and explains the concept in-depth.

Delegates (the definition)

Let’s start with the definition taken from MSDN

“A delegate declaration defines a reference type that can be used to encapsulate a method with a specific signature."

"A delegate instance encapsulates a static or an instance method. Delegates are roughly similar to function pointers in C++; however, delegates are type-safe and secure.”

Following is the list of all the other articles of the OOP series.

Delegates

A delegate is one of the most interesting features of C# programming language. And, it can directly be placed under namespace as classes are placed. Delegates completely follow the rule of object oriented programming. Delegates extend System.Delegate class.

Delegates

Delegates are the best suite for anonymous invocations as they can call any method provided the signature of the method i.e. the return type and the parameters, is the same. Let’s try to cover the topic via practical examples.

Lab 1

Create a console application and name it as per your choice. I named it EventsAndDelegates. Add a public class named DelegateExercises, with the following implementation.

DelegateExercises

  1. using System;  
  2.    
  3. namespace DelegatesAndEvents  
  4. {  
  5.     public class DelegateExercises  
  6.     {  
  7.         public delegate void MyDelegate();  
  8.         void Method1()  
  9.         {  
  10.             Console.WriteLine("Method1");  
  11.             Console.ReadLine();  
  12.         }  
  13.         public void Method2()  
  14.         {  
  15.             MyDelegate myDelegate = new MyDelegate(Method1);  
  16.             myDelegate();  
  17.         }  
  18.    
  19.     }  
  20. }  
Call the method Method2 from Program.cs class.

Program
  1. namespace DelegatesAndEvents {  
  2.     class Program {  
  3.         static void Main(string[] args) {  
  4.             DelegateExercises delegateExercises = new DelegateExercises();  
  5.             delegateExercises.Method2();  
  6.         }  
  7.     }  
  8. }  
Output

Delegates

In the program class, delegateExercises is an instance of DelegateExercises class and delegateExercises.Method2() method is invoked.

While creating the instance, we follow the rule of creating instance using new keyword. In the similar way, we can also use new with delegate name as shown in Method2 of class delegateExercises.

A delegate is very similar to properties or indexers in C#, i.e. it is a first class member of the class. It seems to be a function but is defined with a keyword named delegate.

In the above example of Method2, we created the instance of delegate and passed the whole function i.e. Method1 as a parameter. That means a method itself can also be passed as a parameter using delegates.

This is the way in which C# normally handles call back methods or event handlers. To instantiate a delegate, the traditional “new” keyword is used with one parameter i.e. the method's name itself; “Method1”.

Method1 is the member of class “DelegateExercises” with void return type and taking no parameters. The new keyword as usual creates an object of type delegate, and as we can notice a method, “Method1” is invoked in a nontraditional way i.e. without using Method1() syntax. “myDelegate” is the delegate object of method “Method1”, because the method is passed as a parameter when the object was created. Therefore, when “myDelegate” is called, it means “Method1” is called, thus providing a level of abstraction.

Lab 2

DelegateExercises
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class DelegateExercises {  
  5.         public delegate void MyDelegate();  
  6.         void Method1() {  
  7.             Console.WriteLine("Method1");  
  8.             Console.ReadLine();  
  9.         }  
  10.         public void Method2() {  
  11.             MyDelegate myDelegate = new MyDelegate(Method1);  
  12.             myDelegate(50);  
  13.         }  
  14.   
  15.     }  
  16. }  
Call the method Method2 from Program.cs class.

Program
  1. namespace DelegatesAndEvents {  
  2.     class Program {  
  3.         static void Main(string[] args) {  
  4.             DelegateExercises delegateExercises = new DelegateExercises();  
  5.             delegateExercises.Method2();  
  6.         }  
  7.     }  
  8. }  
Output

Delegates

We get an error “Delegate 'DelegateExercises.MyDelegate' does not take 1 arguments”, when we try to call delegate with a parameter. We passed 50 as a parameter for the method Method1, but you can clearly see that Method1 does not take an integer parameter. Therefore, an error is shown at compilation.

Lab 3

DelegateExercises
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class DelegateExercises {  
  5.         public delegate void MyDelegate();  
  6.         void Method1(int i) {  
  7.             Console.WriteLine("Method1");  
  8.             Console.ReadLine();  
  9.         }  
  10.         public void Method2() {  
  11.             MyDelegate myDelegate = new MyDelegate(Method1);  
  12.             myDelegate();  
  13.         }  
  14.   
  15.     }  
  16. }  
Program
  1. namespace DelegatesAndEvents {  
  2.     class Program {  
  3.         static void Main(string[] args) {  
  4.             DelegateExercises delegateExercises = new DelegateExercises();  
  5.             delegateExercises.Method2();  
  6.         }  
  7.     }  
  8. }  
Output

Delegates

We, again, got an error message, but this is different. We assumed that adding an integer parameter will solve the earlier error, but this error says that the delegate signature should match the method’s signature.

Lab 4
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.   
  5.     public class DelegateExercises {  
  6.         public delegate int MyDelegate(int intValue);  
  7.   
  8.         public int Method1(int intMethod1) {  
  9.             return intMethod1 * 2;  
  10.         }  
  11.   
  12.         public int Method2(int intMethod2) {  
  13.             return intMethod2 * 10;  
  14.         }  
  15.   
  16.         public void Method3() {  
  17.             MyDelegate myDelegate = new MyDelegate(Method1);  
  18.             int result1 = myDelegate(10);  
  19.             System.Console.WriteLine(result1);  
  20.             myDelegate = new MyDelegate(Method2);  
  21.             int result2 = myDelegate(10);  
  22.             System.Console.WriteLine(result2);  
  23.         }  
  24.     }  
  25.   
  26.     public class Program {  
  27.         public static void Main() {  
  28.             DelegateExercises delegateExercises = new DelegateExercises();  
  29.             delegateExercises.Method3();  
  30.             Console.ReadLine();  
  31.   
  32.         }  
  33.     }  
  34. }  
Output

Delegates

To get rid of the error, an integer parameter is added to the delegate MyDelegate. This means the method that will call the delegate will automatically handle this parameter. So, this delegate now returns an integer instead of a void. Finally, when the method is run through delegate, compiler checks for the return parameters too. In Method3, we now make the implementation more tricky, and create one more delegate type having another method name as an argument, and we again execute the delegate with the same parameter having integer value : 10, but the method to be called changes each time, therefore the code could be written more dynamically .

Lab 5
  1. namespace DelegatesAndEvents {  
  2.     public class Program {  
  3.         public static void Main() {  
  4.             DelegateExercises delegateExercises = new DelegateExercises();  
  5.             delegateExercises.Method3();  
  6.         }  
  7.     }  
  8.   
  9.     public class DelegateExercises {  
  10.         public delegate int MyDelegate();  
  11.   
  12.         void Method1() {  
  13.             System.Console.WriteLine("MyDelegate");  
  14.         }  
  15.   
  16.         public void Method3() {  
  17.             MyDelegate myDelegate = new MyDelegate(Method1);  
  18.             myDelegate();  
  19.         }  
  20.     }  
  21. }  
Output

Error 'void DelegateExercises.Method1()' has the wrong return type

Here, compiler finds a mismatch, the return type of the delegate should ideally be integer if used in the context as mentioned in above code, but here Method1 returns void.

Lab 6
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11.   
  12.     public class DelegateExercises {  
  13.         public delegate int MyDelegate(int intValue);  
  14.   
  15.         int Method1(int intMethod1) {  
  16.             return intMethod1 * 2;  
  17.         }  
  18.   
  19.         int Method2(int intMethod1) {  
  20.             return intMethod1 * 10;  
  21.         }  
  22.   
  23.         public void Method4(MyDelegate myDelegate) {  
  24.             int result = myDelegate(10);  
  25.             Console.WriteLine(result);  
  26.         }  
  27.   
  28.         public void Method3() {  
  29.             MyDelegate myDelegate = new MyDelegate(Method1);  
  30.             Method4(myDelegate);  
  31.             myDelegate = new MyDelegate(Method2);  
  32.             Method4(myDelegate);  
  33.         }  
  34.     }  
  35. }  
Output

Delegates

The above written code, just tries to explain that a delegate is nothing but a class. Delegate’s objects could be passed as a parameter as shown in Method4.

Point to remember: Since a delegate is a datatype, it could be passed to a method.

In the first and second invocation of method Method4 , the same delegate is passed a type of MyDelegate. The first time it calls for the method MyDelegate and the second time the method Method2.

Using the object myDelegate, a different method is executed each time. Indirectly a different method is given each time to Method4 as a parameter. This is like writing a generic and abstract code, where Method4 really doesn’t care about how it is called, and what parameters are passed to it. It segregates execution from the implementation.

Lab 7
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11.   
  12.     public class DelegateExercises {  
  13.         public delegate int MyDelegate(int intValue);  
  14.   
  15.         int Method1(int intMethod1) {  
  16.             return intMethod1 * 4;  
  17.         }  
  18.   
  19.         int Method2(int intMethod1) {  
  20.             return intMethod1 * 20;  
  21.         }  
  22.   
  23.         public void Method4(MyDelegate myDelegate) {  
  24.             for (int i = 1; i <= 5; i++)  
  25.                 System.Console.Write(myDelegate(i) + " ");  
  26.         }  
  27.   
  28.         public void Method3() {  
  29.             MyDelegate myDelegate = new MyDelegate(Method1);  
  30.             Method4(myDelegate);  
  31.             myDelegate = new MyDelegate(Method2);  
  32.             Method4(myDelegate);  
  33.         }  
  34.     }  
  35. }  
Output

Delegates

In the above implementation, more segregation and abstraction is achieved. This implementation says that what we are repeating over and over again is that a delegate can also be executed from within a loop construct as well.

A delegate in the above code is passed as an argument to a method, and it executes the name of the method at run time, without knowing its detail at compile time.

Lab 8
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.   
  11.     }  
  12.     public delegate void MyDelegate();  
  13.   
  14.     public class DelegateExercises {  
  15.         void Method1() {  
  16.             System.Console.WriteLine("Method1");  
  17.         }  
  18.         public void Method3() {  
  19.             MyDelegate myDelegate = new MyDelegate(Method1);  
  20.             myDelegate();  
  21.         }  
  22.     }  
  23.   
  24. }  
Output

Delegates

The output is Method1 because MyDelegate being a delegate defines a class that extends System.Delegate.

Lab 9
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public delegate void MyDelegate();  
  5.     public class DelegateExercises: MyDelegate {}  
  6. }  
Output

Error 'DelegateExercises': cannot derive from sealed type 'MyDelegate'

A delegate is represented internally with a class of the same name. In the above code, the class MyDelegate is implicitly sealed and another class cannot derive from a sealed class.

Point to remember: System.Delegate is an abstract class and all the delegates automatically derive from this class.

Lab 10
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11.   
  12.     public delegate void MyDelegate();  
  13.   
  14.     public class DelegateExercises {  
  15.         void Method1() {  
  16.             System.Console.WriteLine("Method1");  
  17.   
  18.   
  19.         }  
  20.   
  21.         public void Method3() {  
  22.             MyDelegate myDelegate = new MyDelegate(Method1);  
  23.             myDelegate();  
  24.             System.Console.WriteLine(myDelegate.ToString());  
  25.         }  
  26.     }  
  27.   
  28.   
  29. }  
Output

Delegates

This simply shows that .ToString() method could be called over delegates as well because System.Delegate also derives from the ultimate base class i.e. Object.

Lab 11
  1. public delegate void MyDelegate();  
  2.   
  3. public class DelegateExercises {  
  4.     void Method3() {  
  5.         System.Console.WriteLine(MyDelegate.ToString());  
  6.     }  
  7. }  
Output

Error An object reference is required for the non-static field, method, or property 'object.ToString()'

The name of the delegate MyDelegate could not be used with .ToString() method because it is not static whereas myDelegate as an object of the class could be used with no errors.

Lab 12
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     using System;  
  5.   
  6.     delegate void ExampleDelegate(string xyz);  
  7.   
  8.     class Program {  
  9.         public static void Method1(string xyz) {  
  10.             Console.WriteLine(xyz + " Method1");  
  11.         }  
  12.   
  13.         public static void Method2(string xyz) {  
  14.             Console.WriteLine(xyz + " Method2");  
  15.         }  
  16.   
  17.         public static void Main() {  
  18.             ExampleDelegate ex1Delegate, ex2Delegate, ex3Delegate, myDelegate;  
  19.   
  20.             ex1Delegate = new ExampleDelegate(Method1);  
  21.             ex2Delegate = new ExampleDelegate(Method2);  
  22.             ex3Delegate = ex1Delegate + ex2Delegate;  
  23.             myDelegate = ex1Delegate - ex2Delegate;  
  24.             ex1Delegate("AAA");  
  25.             ex2Delegate("BBB");  
  26.             ex3Delegate("CCC");  
  27.             myDelegate("DDD");  
  28.             myDelegate = ex3Delegate - ex1Delegate;  
  29.             myDelegate("EEE");  
  30.             myDelegate = ex3Delegate - ex2Delegate;  
  31.             myDelegate("FFF");  
  32.             Console.ReadLine();  
  33.         }  
  34.     }  
  35. }  
Output

Delegates

Now this is a bit more complex scenario, but worth understanding. Instances ex1Delegate, ex2Delegate, ex3Delegate, myDelegate are delegate type objects. Instance ex1Delegate represents Method1 and ex2Delegate represents Method2.

So ex1Delegate("AAA") calls Method1 and ex2Delegate("BBB") calls Method2. Instance ex3Delegate shows the addition of ex1Delegate and ex2Delegate. Here addition does not mean literally adding two delegates as a number, but it means that both the delegates should be invoked and executed.

First, Method1 is called and then Method2, and it is clearly visible in the output as well. Now the instance myDelegate is initialized to ex1Delegate – ex2Delegate, this again is not a mathematical subtraction, but this implementation will remove methods contained in Method2 from Method1.Since we do not have a common method in method2 , it has no significance and Method1 is invoked.

The instance myDelegate is again made equal to ex3Delegate- ex1Delegate. Now this will eliminate all the methods that delegate ex1Delegate represents from object ex3Delegate.

The instance ex3Delegate as shown earlier stands for methods Method1 and Method2, now since ex1Delegate represents method Method1,therefore Method1 gets eliminated from myDelegate. So Method2 is called.

In another case of ex3Delegate – ex2Delegate, had the instance ex3Delegate executed , both Method1 and Method2 would get called. But since we are doing a subtraction, of ex2Delegate, only Method1 is called.

Point to remember, one can call as many methods as we want through Delegates.

Lab 13
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11.   
  12.     public delegate int MyDelegate(out int i);  
  13.   
  14.     public class DelegateExercises {  
  15.         int Method1(out int i) {  
  16.             System.Console.WriteLine("Method1");  
  17.             i = 10;  
  18.             return 0;  
  19.         }  
  20.   
  21.         public void Method3() {  
  22.             MyDelegate myDelegate = new MyDelegate(Method1);  
  23.             MyDelegate myDelegate1 = new MyDelegate(Method1);  
  24.             MyDelegate myDelegate2 = myDelegate + myDelegate1;  
  25.             int intValue;  
  26.             myDelegate2(out intValue);  
  27.   
  28.         }  
  29.     }  
  30.   
  31. }  
Output

Delegates

In the above example we see, that a delegate method could also accept out parameters, and hence we get our result too.

Lab 14
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11.     public delegate int MyDelegate(out int i);  
  12.   
  13.     public class DelegateExercises {  
  14.         int Method1(out int i) {  
  15.             i = 100;  
  16.             System.Console.WriteLine("Method1 " + i);  
  17.             return 0;  
  18.         }  
  19.         public void Method3() {  
  20.             MyDelegate myDelegate = new MyDelegate(Method1);  
  21.             MyDelegate myDelegate1 = null;  
  22.             MyDelegate myDelegate2 = myDelegate + myDelegate1;  
  23.             int intValue;  
  24.             myDelegate2(out intValue);  
  25.         }  
  26.     }  
  27.   
  28. }  
Output

Delegates

The same code works now. The only difference is that one of the delegates is assigned to null. These are the rules of simple delegates, but the rules of a multicast delegate are more constrained.

Two separate delegates can point to a same function and target object. So ‘+’ operator helps us
add delegates and the ‘-’ operator helps us remove one delegate from another.

Lab 15
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             try {  
  8.                 delegateExercises.Method3();  
  9.                 Console.ReadLine();  
  10.             } catch (System.Exception ex) {  
  11.                 System.Console.WriteLine("Exception Occurred.");  
  12.                 Console.ReadLine();  
  13.   
  14.             }  
  15.         }  
  16.     }  
  17.   
  18.     public delegate void MyDelegate();  
  19.   
  20.     public class DelegateExercises {  
  21.         void Method1() {  
  22.             throw new System.Exception();  
  23.         }  
  24.   
  25.         public void Method3() {  
  26.             MyDelegate myDelegate = new MyDelegate(Method1);  
  27.             myDelegate();  
  28.         }  
  29.     }  
  30.   
  31. }  
Output

Delegates

In the above code, myDelegate() calls method Method1.

This method throws an exception. We choose not to handle this exception in Method3 but in Main method where Method3 is called.

We see that exception caused by a delegate keeps on propagating until it is caught.

Lab 16
  1. using System;  
  2.   
  3. namespace DelegatesAndEvents {  
  4.     public class Program {  
  5.         public static void Main() {  
  6.             DelegateExercises delegateExercises = new DelegateExercises();  
  7.             delegateExercises.Method3();  
  8.             Console.ReadLine();  
  9.   
  10.         }  
  11.     }  
  12.   
  13.     public delegate void MyDelegate(ref int intValue);  
  14.   
  15.     public class DelegateExercises {  
  16.         void Method1(ref int intValue) {  
  17.             intValue = intValue + 5;  
  18.             System.Console.WriteLine("Method1 " + intValue);  
  19.         }  
  20.   
  21.         public void Method3() {  
  22.             MyDelegate myDelegate = new MyDelegate(Method1);  
  23.             MyDelegate myDelegate1 = new MyDelegate(Method1);  
  24.             MyDelegate myDelegate2 = myDelegate + myDelegate1;  
  25.             int intParameter = 5;  
  26.             myDelegate2(ref intParameter);  
  27.         }  
  28.     }  
  29.   
  30. }  
Output

Delegates

In the earlier example, we saw ‘out’ parameter and came to a conclusion that they are not used in multicast delegates. Whereas in the above example we see, that passing the parameter by ‘ref’ is allowed in multicast delegate.

Conclusion

This article covered the topic of Delegate in detail. Delegates are very crucial to understand but are tricky to implement. I hope this post helped the readers to get an insight into delegates.

Read more

For more technical articles you can reach out to CodeTeddy

My other series of articles,

Happy coding !