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:
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
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:
Standard Delegates (Custom/User-Defined)
Generic Delegates (Func, Action, Predicate)
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);
public
Makes the delegate accessible from anywhere (other classes, projects, or assemblies).
delegate
Defines a delegate type — a method contract, not an actual method implementation.
void
Specifies that any method assigned to this delegate must not return a value.
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 Type | Purpose | Return Type | Arguments | Syntax | Syntax Explanation | Real-World Example |
|---|
| Action | Used for methods that perform an operation without returning a value | void | 0 to 16 | Action<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. |
| Func | Used for methods that return a value | Last generic type (TResult) | 0 to 16 input types | Func<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. |
| Predicate | Specialized delegate for boolean conditions | bool | Exactly 1 | Predicate<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:
Save Data Subsystem
Close Network Connections Subsystem
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);
public
Makes the delegate accessible from anywhere in the application or other assemblies.
delegate
Declares a delegate type, which defines a method signature (contract) instead of a method implementation.
void
Specifies that any method assigned to this delegate must not return any value.
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.