SoFunction
Updated on 2025-03-06

Basic learning tutorial on delegate delegation types in C#

Entrustment
delegate is a type that represents a reference to a method with a specific parameter list and return type. When instantiating a delegate, you can associate its instance with any method with compatible signatures and return types. You can call methods by delegating instances.
Delegate is used to pass methods as parameters to other methods. The event handler is a delegate call method. You can create a custom method that can call your method when a specific event occurs. The following example demonstrates a delegate declaration:

public delegate int PerformCalculation(int x, int y);

Any method in any accessible class or structure that matches the delegate type can be assigned to the delegate. The method can be a static method or an instance method. This allows you to programmatically change method calls and insert new code into existing classes.
Note: In the context of method overloading, the signature of the method does not include the return value. But in the context of the delegate, the signature includes the return value. In other words, the method and the delegate must have the same return type.
The ability to reference methods as parameters makes delegates ideal for defining callback methods. For example, a reference to a method that compares two objects can be passed as a parameter into the sorting algorithm. Since comparing the code is in a separate process, sorting algorithms can be written in more common ways.
Entrustment Overview
Delegates have the following properties:

  1. Delegates are similar to C++ function pointers, but they are type-safe.
  2. Delegation allows methods to be passed as parameters.
  3. Delegates can be used to define callback methods.
  4. Delegates can be linked together; for example, multiple methods can be called for one event.
  5. The method does not have to exactly match the delegate type.
  6. C# version 2.0 introduces the concept of anonymous methods, which allow passing code blocks as parameters instead of individually defined methods. C# 3.0 introduces Lambda expressions, which allow you to write inline code blocks more concisely. Both anonymous methods and Lambda expressions (in some contexts) can be compiled into delegate types. These functions are now collectively referred to as anonymous functions.

Use Delegate

Delegates are the type of secure encapsulation methods, similar to function pointers in C and C++. Unlike C function pointers, delegates are object-oriented, type-safe, and reliable. The type of delegation is determined by the name of delegation. The following example declares a delegate named Del that can encapsulate a method that takes a string as a parameter and returns void:

public delegate void Del(string message);

Delegate objects are usually constructed by providing the name of the method that the delegate will encapsulate or using an anonymous method. After instantiating the delegate, the delegate passes the method call it made to the method. The parameter passed by the caller to the delegate is passed to the method, and the delegate returns the return value of the method (if any) to the caller. This is called a call delegate. The instantiated delegate can be called according to the encapsulated method itself. For example:

 // Create a method for a delegate.
public static void DelegateMethod(string message)
{
  (message);
}


 // Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");


The delegate type is derived from the Delegate class in the .NET Framework. Delegate types are encapsulated, they cannot derive other classes, nor can they derive custom classes from Delegate. Since an instantiated delegate is an object, it can be passed as a parameter or assigned to a property. This allows the method to accept the delegate as a parameter and to call the delegate later. This is called an asynchronous callback, and is a common method to notify the caller when a long process is completed. When using a delegate in this way, the code using the delegate does not need to know the implementation method to be used. Functions are similar to those provided by the package interface.
Another common use of callbacks is to define a custom comparison method and pass that delegate to a short method. It allows the caller's code to be part of the sorting algorithm. The following example method uses the Del type as a parameter:

public void MethodWithCallback(int param1, int param2, Del callback)
{
  callback("The number is: " + (param1 + param2).ToString());
}

Then you can pass the delegate created above to the method:

MethodWithCallback(1, 2, handler);

And receive the following output to the console:

The number is: 3

When using delegates in an abstract way, MethodWithCallback does not need to call the console directly, remember that it does not have to be designed to have a console. The purpose of MethodWithCallback is to simply prepare the string and pass the string to other methods. This function is particularly powerful because the delegated method can use any number of parameters.
When the delegate is constructed as an encapsulated instance method, the delegate will reference both the instance and the method. The delegate does not know the instance type other than the method it encapsulates, so the delegate can refer to any type of object as long as there is a method on that object that matches the delegate signature. When a delegate is constructed to encapsulate a static method, the delegate refers only to the method. Please consider the following statement:

public class MethodClass
{
  public void Method1(string message) { }
  public void Method2(string message) { }
}

Adding to the static DelegateMethod shown previously, we now have three methods that Del instances can encapsulate.
When called, the delegate can call multiple methods. This is called multicast. To add additional methods to the delegate's method list (the call list), simply add two delegates using the addition operator or the addition assignment operator ("+" or "+="). For example:

MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

At this time, the call list of allMethodsDelegate contains three methods, namely Method1, Method2 and DelegateMethod. The original three delegations (d1, d2 and d3) remain unchanged. When allMethodsDelegate is called, all three methods are called in order. If the delegate uses the reference parameters, the reference will be passed to all three methods in the reverse order, and any changes made by one method will be seen on the other. When a method raises an exception that is not caught within the method, the exception is passed to the delegated caller and subsequent methods in the call list are not called. If the delegate has a return value and/or output parameter, it returns the return value and parameter of the last method called. To delete a method in the call list, use the subtraction operator or the subtraction assignment operator ("+" or "+="). For example:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;

Since the delegate type is derived from , the methods and properties defined by the class can be called on the delegate. For example, to query the number of methods in the delegate call list, you could write:

int invocationCount = ().GetLength(0);

Delegates with multiple methods in the call list are derived from MulticastDelegate, which belongs to a subclass of . Since both classes support GetInvocationList, the above code will also work in other cases.
Multicast delegates are widely used in event processing. The event source object sends event notifications to the recipient object registered to receive the event. To register an event, the receiver needs to create a method for processing the event, then create a delegate for the method and pass the delegate to the event source. When an event occurs, the source calls the delegate. The delegate will then call the event handling method on the receiver, thereby providing the event data. The delegate type of a given event is determined by the event source. For more information, see Events (C# Programming Guide).
Comparing two different types of delegates assigned at compile time will result in a compile error. If the delegate instance is of a static type, comparison is allowed, but false will be returned at runtime. For example:

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e,  f)
{
  // Compile-time error.
  //(d == e);

  // OK at compile-time. False if the run-time type of f 
  // is not the same as that of d.
  (d == f);
}


Delegates with named methods vs. Delegates with anonymous methods

Delegates can be associated with named methods. When instantiating a delegate using a named method, the method is passed as a parameter, for example:

// Declare a delegate:
delegate void Del(int x);

// Define a named method:
void DoWork(int k) { /* ... */ }

// Instantiate the delegate using the method as a parameter:
Del d = ;

This is called using naming. Delegates constructed using named methods can encapsulate static methods or instance methods. In earlier versions of C#, naming methods were the only way to instantiate delegates. However, when you don't want to pay the system overhead of creating a new method, C# allows you to instantiate the delegate and immediately specify the block of code that the delegate will process when it is called. Code blocks can contain lambda expressions or anonymous methods.

Note: The method passed as a delegate parameter must have the same signature as the delegate declaration.
Delegated instances can encapsulate static or instance methods.
Although the delegate can use the out parameter, it is recommended that you do not use it for a multicast event delegate, because you cannot know which delegate will be called.
Example 1
Here is a simple example of a declaration and use of a delegation. Note that delegate Del and associated methods MultiplyNumbers have the same signature

// Declare a delegate
delegate void Del(int i, double j);

class MathClass
{
  static void Main()
  {
    MathClass m = new MathClass();

    // Delegate instantiation using "MultiplyNumbers"
    Del d = ;

    // Invoke the delegate object.
    ("Invoking the delegate using 'MultiplyNumbers':");
    for (int i = 1; i <= 5; i++)
    {
      d(i, 2);
    }

    // Keep the console window open in debug mode.
    ("Press any key to exit.");
    ();
  }

  // Declare the associated method.
  void MultiplyNumbers(int m, double n)
  {
    (m * n + " ");
  }
}

Output:

  Invoking the delegate using 'MultiplyNumbers':
  2 4 6 8 10

Example 2
In the following example, a delegate is mapped to both the static method and the instance method and returns specific information respectively.

// Declare a delegate
delegate void Del();

class SampleClass
{
  public void InstanceMethod()
  {
    ("A message from the instance method.");
  }

  static public void StaticMethod()
  {
    ("A message from the static method.");
  }
}

class TestSampleClass
{
  static void Main()
  {
    SampleClass sc = new SampleClass();

    // Map the delegate to the instance method:
    Del d = ;
    d();

    // Map to the static method:
    d = ;
    d();
  }
}

Output:

  A message from the instance method.
  A message from the static method.