Click here to Skip to main content
15,867,870 members
Articles / Programming Languages / C#

Learning C# (Day 10): Delegates in C# (A Practical Approach)

Rate me:
Please Sign up or sign in to vote.
4.79/5 (21 votes)
7 Nov 2016CPOL9 min read 42.9K   924   47   11
This article of the series “Diving into OOP” will explain all about delegates in C#. The article focuses more on practical implementations and less on theory. The article explains the concept in-depth.

Table of Contents

Introduction

This article of the series "Diving into OOP" will explain all about delegates in C#. The article focuses more on practical implementations and less on theory. The article 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."

Other Articles of the series

The following is the list of all the other articles of the OOP series.

Delegates

A delegate is one of the most interesting features of the 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 the System.Delegate class.

Image 1

Delegates are the best suit for anonymous invocations as they can call any method provided in 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 what you want, I named it EventsAndDelegates. Add a public class named DelegateExercises with the following implementation.

DelegateExercises

C#
using System;
 
namespace DelegatesAndEvents
{
    public class DelegateExercises
    {
        public delegate void MyDelegate();
        void Method1()
        {
            Console.WriteLine("Method1");
            Console.ReadLine();
        }
        public void Method2()
        {
            MyDelegate myDelegate = new MyDelegate(Method1);
            myDelegate();
        }
 
    }
}

Call the method Method2 from Program.cs class.

Program

C#
namespace DelegatesAndEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            DelegateExercises delegateExercises=new DelegateExercises();
            delegateExercises.Method2();
        }
    }
}

Output

Image 2

In the program class, delegateExercises is an instance of the DelegateExercises class and the delegateExercises.Method2() method is invoked. While creating the instance we follow the rule of creating instances using a new keyword. In a similar way, we can also use new with delegate name as shown in Method2 of the 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 callback methods or event handlers. To instantiate a delegate, the traditional "new" keyword is used with one parameter i.e. the methods 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 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

C#
using System;
 
namespace DelegatesAndEvents
{
    public class DelegateExercises
    {
        public delegate void MyDelegate();
        void Method1()
        {
            Console.WriteLine("Method1");
            Console.ReadLine();
        }
        public void Method2()
        {
            MyDelegate myDelegate = new MyDelegate(Method1);
            myDelegate(50);
        }
 
    }
}

Call the method Method2 from the Program.cs class.

Program

C#
namespace DelegatesAndEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            DelegateExercises delegateExercises=new DelegateExercises();
            delegateExercises.Method2();
        }
    }
}

Output

Image 3

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 takes an integer parameter, therefore an error is shown at compilation.

Lab 3

DelegateExercises

C#
using System;
 
namespace DelegatesAndEvents
{
    public class DelegateExercises
    {
        public delegate void MyDelegate();
        void Method1(int i)
        {
            Console.WriteLine("Method1");
            Console.ReadLine();
        }
        public void Method2()
        {
            MyDelegate myDelegate = new MyDelegate(Method1);
            myDelegate();
        }
 
    }
}

Program

C#
namespace DelegatesAndEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            DelegateExercises delegateExercises=new DelegateExercises();
            delegateExercises.Method2();
        }
    }
}

Output

Image 4

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

C#
using System;
 
namespace DelegatesAndEvents
{
 
  public class DelegateExercises
  {
    public delegate int MyDelegate(int intValue);
 
    public int Method1(int intMethod1)
    {
      return intMethod1*2;
    }
 
    public int Method2(int intMethod2)
    {
      return intMethod2*10;
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      int result1 = myDelegate(10);
      System.Console.WriteLine(result1);
      myDelegate = new MyDelegate(Method2);
      int result2 = myDelegate(10);
      System.Console.WriteLine(result2);
    }
  }
 
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
 
    }
  }
}

Output

Image 5

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, the compiler checks for the return patrameters 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

C#
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
    }
  }
 
  public class DelegateExercises
  {
    public delegate int MyDelegate();
 
    void Method1()
    {
      System.Console.WriteLine("MyDelegate");
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      myDelegate();
    }
  }
}

Output

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

Here the 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 Method1 returns void.

Lab 6

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
  }
 
  public class DelegateExercises
  {
    public delegate int MyDelegate(int intValue);
 
    int Method1(int intMethod1)
    {
      return intMethod1*2;
    }
 
    int Method2(int intMethod1)
    {
      return intMethod1*10;
    }
 
    public void Method4(MyDelegate myDelegate)
    {
      int result = myDelegate(10);
      Console.WriteLine(result);
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      Method4(myDelegate);
      myDelegate = new MyDelegate(Method2);
      Method4(myDelegate);
    }
  }
}

Output

Image 6

The above written code just tries to explain that a delegate is nothing but a class. A 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 it calls for 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

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
  }
 
  public class DelegateExercises
  {
    public delegate int MyDelegate(int intValue);
 
    int Method1(int intMethod1)
    {
      return intMethod1*4;
    }
 
    int Method2(int intMethod1)
    {
      return intMethod1*20;
    }
 
    public void Method4(MyDelegate myDelegate)
    {
      for (int i = 1; i <= 5; i++)
        System.Console.Write(myDelegate(i) + " ");
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      Method4(myDelegate);
      myDelegate = new MyDelegate(Method2);
      Method4(myDelegate);
    }
  }
}

Output

Image 7

In the above implementation, more segregation and abstraction is achieved. This implementation says, "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

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
 
  }
  public delegate void MyDelegate();

  public class DelegateExercises
  {
    void Method1()
    {
      System.Console.WriteLine("Method1");
    }
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      myDelegate();
    }
  }
 
}

Output

Image 8

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

Lab 9

C#
using System;
 
namespace DelegatesAndEvents
{
  public delegate void MyDelegate();
  public class DelegateExercises : MyDelegate
  {
  }
}

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

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
  }
 
  public delegate void MyDelegate();
 
  public class DelegateExercises
  {
    void Method1()
    {
      System.Console.WriteLine("Method1");
 
 
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      myDelegate();
      System.Console.WriteLine(myDelegate.ToString());
    }
  }
 
 
}

Output

Image 9

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

Lab 11

C#
public delegate void MyDelegate();
 
 public class DelegateExercises
 {
   void Method3()
   {
     System.Console.WriteLine(MyDelegate.ToString());
   }
 }

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

C#
using System;
 
namespace DelegatesAndEvents
{
  using System;
 
  delegate void ExampleDelegate(string xyz);
 
  class Program
  {
    public static void Method1(string xyz)
    {
      Console.WriteLine(xyz + " Method1");
    }
 
    public static void Method2(string xyz)
    {
      Console.WriteLine(xyz + " Method2");
    }
 
    public static void Main()
    {
      ExampleDelegate ex1Delegate, ex2Delegate, ex3Delegate, myDelegate;
 
      ex1Delegate = new ExampleDelegate(Method1);
      ex2Delegate = new ExampleDelegate(Method2);
      ex3Delegate = ex1Delegate + ex2Delegate;
      myDelegate = ex1Delegate - ex2Delegate;
      ex1Delegate("AAA");
      ex2Delegate("BBB");
      ex3Delegate("CCC");
      myDelegate("DDD");
      myDelegate = ex3Delegate - ex1Delegate;
      myDelegate("EEE");
      myDelegate = ex3Delegate - ex2Delegate;
      myDelegate("FFF");
      Console.ReadLine();
    }
  }
}

Output

Image 10

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 we want through Delegates.

Lab 13

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
  }
 
  public delegate int MyDelegate(out int i);
 
  public class DelegateExercises
  {
    int Method1(out int i)
    {
      System.Console.WriteLine("Method1");
      i = 10;
      return 0;
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      MyDelegate myDelegate1 = new MyDelegate(Method1);
      MyDelegate myDelegate2 = myDelegate + myDelegate1;
      int intValue;
      myDelegate2(out intValue);
 
    }
  }
 
}

Output

Image 11

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

Lab 14

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
    }
  }
  public delegate int MyDelegate(out int i);

  public class DelegateExercises
  {
    int Method1(out int i)
    {
      i = 100;
      System.Console.WriteLine("Method1 " + i);
      return 0;
    }
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      MyDelegate myDelegate1 = null;
      MyDelegate myDelegate2 = myDelegate + myDelegate1;
      int intValue;
      myDelegate2(out intValue);
    }
  }
 
 }

Output

Image 12

The same code works now. The only difference is that one of the delegate 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

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      try
      {
        delegateExercises.Method3();
        Console.ReadLine();
      }
      catch (System.Exception ex)
      {
        System.Console.WriteLine("Exception Occurred.");
        Console.ReadLine();
 
      }
    }
  }
 
  public delegate void MyDelegate();
 
  public class DelegateExercises
  {
    void Method1()
    {
      throw new System.Exception();
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      myDelegate();
    }
  }
 
}

Output

Image 13

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

This method throws an exception. We choose not to handle this exception in Method3 but in the Main method where Method3 is called. We see that exception caused by a delegate keeps on propagating until it is caught.

Lab 16

C#
using System;
 
namespace DelegatesAndEvents
{
  public class Program
  {
    public static void Main()
    {
      DelegateExercises delegateExercises = new DelegateExercises();
      delegateExercises.Method3();
      Console.ReadLine();
 
    }
  }
 
  public delegate void MyDelegate(ref int intValue);
 
  public class DelegateExercises
  {
    void Method1(ref int intValue)
    {
      intValue = intValue + 5;
      System.Console.WriteLine("Method1 " + intValue);
    }
 
    public void Method3()
    {
      MyDelegate myDelegate = new MyDelegate(Method1);
      MyDelegate myDelegate1 = new MyDelegate(Method1);
      MyDelegate myDelegate2 = myDelegate + myDelegate1;
      int intParameter = 5;
      myDelegate2(ref intParameter);
    }
  }
 
}

Output

Image 14

In the earlier example, we saw the ‘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 of delegates.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect https://codeteddy.com/
India India
Akhil Mittal is two times Microsoft MVP (Most Valuable Professional) firstly awarded in 2016 and continued in 2017 in Visual Studio and Technologies category, C# Corner MVP since 2013, Code Project MVP since 2014, a blogger, author and likes to write/read technical articles, blogs, and books. Akhil is a technical architect and loves to work on complex business problems and cutting-edge technologies. He has an experience of around 15 years in developing, designing, and architecting enterprises level applications primarily in Microsoft Technologies. He has diverse experience in working on cutting-edge technologies that include Microsoft Stack, AI, Machine Learning, and Cloud computing. Akhil is an MCP (Microsoft Certified Professional) in Web Applications and Dot Net Framework.
Visit Akhil Mittal’s personal blog CodeTeddy (CodeTeddy ) for some good and informative articles. Following are some tech certifications that Akhil cleared,
• AZ-304: Microsoft Azure Architect Design.
• AZ-303: Microsoft Azure Architect Technologies.
• AZ-900: Microsoft Azure Fundamentals.
• Microsoft MCTS (70-528) Certified Programmer.
• Microsoft MCTS (70-536) Certified Programmer.
• Microsoft MCTS (70-515) Certified Programmer.

LinkedIn: https://www.linkedin.com/in/akhilmittal/
This is a Collaborative Group

779 members

Comments and Discussions

 
QuestionExcellent article Pin
udeep kansal21-Dec-16 23:36
professionaludeep kansal21-Dec-16 23:36 
AnswerRe: Excellent article Pin
Akhil Mittal22-Dec-16 2:02
professionalAkhil Mittal22-Dec-16 2:02 
QuestionAbout Real Practical Example Pin
Member 1254009621-Nov-16 23:36
Member 1254009621-Nov-16 23:36 
GeneralMy vote of 5 Pin
D V L15-Nov-16 19:55
professionalD V L15-Nov-16 19:55 
GeneralRe: My vote of 5 Pin
Akhil Mittal29-Nov-16 0:58
professionalAkhil Mittal29-Nov-16 0:58 
GeneralMy vote of 5 Pin
Member 121773159-Nov-16 20:34
Member 121773159-Nov-16 20:34 
GeneralRe: My vote of 5 Pin
Akhil Mittal10-Nov-16 19:09
professionalAkhil Mittal10-Nov-16 19:09 
PraiseMy vote of 5 Pin
Marcel Kalinowski8-Nov-16 20:22
Marcel Kalinowski8-Nov-16 20:22 
GeneralRe: My vote of 5 Pin
Akhil Mittal8-Nov-16 20:57
professionalAkhil Mittal8-Nov-16 20:57 
PraiseMy vote of 5 Pin
Vikas Sharma7-Nov-16 20:29
professionalVikas Sharma7-Nov-16 20:29 
GeneralRe: My vote of 5 Pin
Akhil Mittal8-Nov-16 0:49
professionalAkhil Mittal8-Nov-16 0:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.