C#  

Understanding Delegates in C#

Delegates are a fundamental and powerful concept in .NET programming, serving as type-safe function pointers. They allow methods to be treated as objects, which is crucial for flexible and extensible application design.

Delegates in C#

A delegate in C# is a type that represents a reference to a method.
It is like storing a method inside a variable, so that the method can be passed around, executed later, or dynamically changed at runtime.

In simple terms:

  • A delegate = a container that holds a method

  • The method inside can be swapped, changed, or triggered at any time

  • The program decides at runtime what method to execute

Imagine a large company building a notification system.

The system must send notifications through different channels:

  • SMS

  • Email

  • WhatsApp

  • Push Notification

But the system should not hard-code which notification to send.
It should plug-in the required method at runtime.

A delegate works like a plug-point:

  • The plug point stays the same (delegate)

  • Different plugs can be connected (different methods)

  • The system does not care which plug is connected — it only triggers the delegate

This allows the application to be flexible, extendable, and cleanly designed.

Delegates

How to declare and use a delegate in C#

1. Step: Declare the Delegate (the plug-point)

A delegate simply defines the shape of the method:

public delegate void NotificationDelegate(string message);

This means:

  • The delegate can point to any method

  • That method must accept string and return void

2. Step: Create Notification Methods (the plugs)

public class NotificationService

{

    public void SendSMS(string msg)

        => Console.WriteLine("SMS: " + msg);

    public void SendEmail(string msg)

        => Console.WriteLine("Email: " + msg);

    public void SendWhatsApp(string msg)

        => Console.WriteLine("WhatsApp: " + msg);

    public void SendPush(string msg)

        => Console.WriteLine("Push Notification: " + msg);

}

All methods match the delegate's shape:

  • one string input

  • returns void

So the delegate can call any of these.

3. Step: Assign Delegate at Runtime (plugging in the method)

class Program

{

    static void Main()

    {

        var service = new NotificationService();

        // Plug Email method into delegate

        NotificationDelegate notifier = service.SendEmail;

        notifier("Server is down!");

        // Change plug to SMS

        notifier = service.SendSMS;

        notifier("Payment received!");

        // Change plug to WhatsApp

        notifier = service.SendWhatsApp;

        notifier("Meeting at 3 PM");

        // Change plug to Push Notification

        notifier = service.SendPush;

        notifier("New login detected");

    }

}

Output

Email: Server is down!

SMS: Payment received!

WhatsApp: Meeting at 3 PM

Push Notification: New login detected

Why this output appears

  • First, the delegate is assigned to SendEmail()

  • Then it is changed to SendSMS()

  • Then to SendWhatsApp()

  • Finally to SendPush()

Each time the delegate calls the method that is currently plugged in.

Types of Delegates in C#

we can categorize C# delegates into three main types based on how they are defined and used:

  1. Standard Delegates (Custom/User-Defined)

  2. Generic Delegates (Func, Action, Predicate)

  3. Multicast Delegates

1. Standard Delegates (Custom/User-Defined)

These are the foundational delegates. They require an explicit definition using the delegate keyword, similar to defining a class or an interface. A standard delegate defines a specific, named signature. It serves as a blueprint. Once defined, variables of this delegate type can hold a reference to any method that adheres to that exact signature.

Imagine a "Maintenance Notification" delegate. The contract states: "A message must be delivered, and the process must return a success/failure status." Any maintenance team (method) that can accept a message string and return a boolean status (success/failure) can be plugged into this notification system.

Syntax

This delegate can reference any public or private method that takes a string parameter and returns void.

public delegate void NotificationDelegate(string message);
  1. public
    Makes the delegate accessible from anywhere (other classes, projects, or assemblies).

  2. delegate
    Defines a delegate type — a method contract, not an actual method implementation.

  3. void
    Specifies that any method assigned to this delegate must not return a value.

  4. NotificationDelegate(string message);

  • NotificationDelegate is the delegate name (type)

  • It accepts one string parameter named message

  • Any matching method can be assigned to it

2. Generic Delegates (Func, Action, Predicate)

The .NET framework provides a set of built-in, generic delegates that cover the most common method signatures. These should be preferred over defining custom delegates, as they reduce boilerplate code and improve consistency.

These are pre-defined delegates, so there's no need to use the delegate keyword. They use generic type parameters (<T>) to specify the types of the parameters and the return value.

Delegate TypePurposeReturn TypeArgumentsSyntaxSyntax ExplanationReal-World Example
ActionUsed for methods that perform an operation without returning a valuevoid0 to 16Action<T1, T2>T1, T2 represent input parameter types. Since Action returns void, no return type is specified.Log Message – The system asks a method to write a log entry, but does not expect any result back.
FuncUsed for methods that return a valueLast generic type (TResult)0 to 16 input typesFunc<T1, T2, TResult>T1, T2 are input parameters and TResult defines the return type. The last type parameter always represents the output.Calculate Tax – The system provides an amount and expects the calculated tax value in return.
PredicateSpecialized delegate for boolean conditionsboolExactly 1Predicate<T>T represents the input parameter type. The delegate always returns true or false.Data Validator – The system checks if input data is valid (e.g., age ≥ 18) and expects a yes/no answer.

3. Multicast Delegates

A delegate instance that references multiple methods is called a multicast delegate. A multicast delegate can hold multiple method references and invoke them in sequence. Commonly used in event handling.

When a multicast delegate is invoked, it calls the referenced methods sequentially, in the order they were added. The delegate maintains a list of method references, often called an Invocation List.

  • For delegates with a void return type (Action or a custom void delegate), all methods are executed.

  • For delegates with a non-void return type (Func or a custom returning delegate), all methods are still executed, but only the return value of the last method executed is returned to the caller. The return values of the preceding methods are discarded.

Consider a "System Shutdown" delegate. Multiple subsystems need to be notified when the system is shutting down:

  1. Save Data Subsystem

  2. Close Network Connections Subsystem

  3. Display Shutdown Message Subsystem

All three methods are added to the delegate. When the delegate is invoked, all three actions execute in sequence.

Syntax

public delegate void AlertDelegate(string message);
  1. public
    Makes the delegate accessible from anywhere in the application or other assemblies.

  2. delegate
    Declares a delegate type, which defines a method signature (contract) instead of a method implementation.

  3. void
    Specifies that any method assigned to this delegate must not return any value.

  4. AlertDelegate(string message);
    Defines the delegate name and its parameter list.
    Any method matching this signature (one string parameter and void return type) can be assigned to it.

Conclusion

Delegates are type-safe method references that allow methods to be passed and invoked dynamically. They enable loose coupling, making code more flexible and reusable. Built-in delegates like Action, Func, and Predicate simplify common patterns.

Multicast delegates and events allow one-to-many method calls for notifications and event handling. Mastering delegates is essential for understanding events, callbacks, LINQ, and modern C# programming.