Search
Explore digital transformation resources.
Uncover insights, best practises and case studies.
Search
Explore digital transformation resources.
Uncover insights, best practises and case studies.
Service
Industry
Delegates are a powerful feature in C#—one that helps you write simpler, more concise code. But they can also be a bit tricky to grasp. Whether it’s the syntax, lambda expressions, or the idea of treating methods as variables, you’re not alone if you’ve ever struggled to understand how delegates work—or why they matter.
That’s exactly why we put together this guide. We’ll define what delegates are, explain how they work, and show you how to use them effectively to write better C# code.
Let’s start from the ground up!
First things first—let's define what a delegate is. In short, a delegate is a type that holds references to methods with a specific signature, including parameter types and return type. Think of it as a function pointer but type-safe. It allows methods to be passed as arguments and executed dynamically at runtime.
For example, suppose you're building a notification system where messages can be sent through different channels—email, SMS, or push notifications. Instead of hardcoding each method call, you can use a delegate to determine which method to execute at runtime. This keeps your code flexible. Plus, if you later add a new notification type, you won't need to modify the existing logic; just assign the new method to the delegate. For more practical examples, this detailed guide covers additional use cases and real-world applications.
Delegates offer several practical benefits in C# development:
Delegates provide a type-safe way to handle method references, ensuring that the method signature matches the delegate signature.
Type Safety
They allow methods to be passed as parameters, enabling dynamic method invocation and callback mechanisms.
Flexibility
Delegates are the backbone of event handling in C#. They simplify UI interactions, asynchronous operations, and custom event-driven logic by allowing multiple methods to be executed in response to an event.
Event Handling
Instead of relying on repetitive if-else or switch statements, delegates enable method selection at runtime, leading to cleaner, more modular, and maintainable code.
Code Reusability & Maintainability
We've defined delegates and explored why they're helpful. Now, let's look at the different types of delegates that C# provides:
A single cast delegate holds a reference to one method at a time. When invoked, it executes only that method. This makes it ideal when there's a direct, one-to-one relationship between an event and its handler.
Because it can store only one method, assigning a new one replaces the previous reference. This ensures that only the most recent assignment runs, preventing unintended multiple-method calls. As a result, single cast delegates provide a clear, simple, and predictable execution flow.
A multicast delegate holds references to multiple methods and executes them in sequence when invoked. This makes it especially useful in event-driven programming, where multiple subscribers need to respond to the same action.
Since multicast delegates maintain an invocation list, every referenced method runs in order when the delegate is called. That's why they require careful management to avoid unexpected behavior from multiple subscribers—for example, issues related to execution order or error handling.
.NET provides built-in generic delegates like Func<>, Action<>, and Predicate<>. These simplify the use of delegates in common scenarios such as performing operations, executing void methods, and applying conditional checks. Working with them can spare you the work of declaring custom delegate types and make your code more concise and reusable. We’ll look at generic delegates in more detail below. In the meantime, this article offers a helpful breakdown.
Declaring and instantiating delegates are essential steps in working with delegates. While declaring a delegate defines a blueprint for methods it can reference, instantiating a delegate creates an instance that connects to a specific method.
Let's break down these procedures.
First, we'll look at the specific structure and rules you need to follow when declaring a delegate in C#. What elements must it include?
So, the syntax follows this structure:
[modifier] delegate [return_type] [delegate_name] ([parameter_list]);
Defines the delegate's access level (e.g., public, private). This is optional.
The keyword that declares a delegate.
Specifies the type of value returned by methods assigned to the delegate. It can be void.
The user-defined identifier for the delegate.
Specifies the parameters required by the delegate's target methods.
Now that we've defined a delegate, the next step is to create an instance and link it to a method. This connects the delegate to a specific function that matches its signature, allowing it to be invoked like a regular method.
To create an instance of a delegate
The syntax should look like this:
[delegate_type] [delegate_instance] = [method_name];
Once declared and instantiated, the delegate is ready to be called—just like any regular method.
Related read: How to declare, instantiate, and use a Delegate - C# Programming Guide.
Use the delegate type as a variable.
Set it equal to a method that matches the delegate's signature.
Assign the method name without parentheses.
Instead of referencing an existing method, define the function inline.
Once a delegate is instantiated, it can be invoked to execute the method it references. Invoking a delegate works just like calling a regular method, but with added flexibility—you can change the assigned method at runtime or pass delegates as parameters. There are different ways to invoke a delegate.
Direct invocation
The simplest way to invoke a delegate is by calling it like a method. If the delegate has parameters, you pass them as you would in a regular method call. This is called direct invocation because the delegate is executed immediately.
Using delegate instances
A delegate instance can be invoked using its Invoke() method. While the behavior is identical to direct invocation, explicitly calling Invoke can be helpful in some scenarios—especially when you combine it with the null-conditional operator (?.) to avoid runtime errors.
For example, instead of writing myDelegate(), you can write myDelegate?.Invoke() to safely invoke the delegate only if it’s not null.
Advanced delegate concepts: events and lambda expressions.
Many of us feel that our local public services are facing significant challenges. At the more trivial end, my street isn’t swept as often, and I’m now paying for garden waste collection for the first time. However, a much more serious issue is the lack of coordination between health and social care services, which I experienced last year. This almost led to my dad being critically neglected, had I not been there to join the dots between different agencies.
As we approach the Autumn Budget and 2025 Spending Review, the pressure on public services has never been greater.
Local authorities are grappling with severe financial strain, driven by inflation, wage pressures, and increasing demand for services. The financial sustainability of many councils is at risk, with some already reliant on exceptional financial support from Central Government. The “Place-Based Public Service Budgets” report published by New Local earlier this year highlighted issues like fragmented funding, short-term crisis management, and a lack of coordination between services. These problems make it tough for local authorities to deliver effective, long-term solutions for their communities.
Events in C# build on top of delegates to enable a clean publisher-subscriber pattern. They allow one part of your program to notify others when something happens—like a button click or a data update. The event keyword enforces encapsulation: other classes can subscribe or unsubscribe, but only the class that declares the event can trigger it. This makes your code more robust and reduces coupling between components.
Let’s see an example:
A class defines an event, and another part of the code subscribes to it using a method or a lambda expression.
For instance, a Button class might have an OnClick event. You can subscribe to it like this: button.OnClick += () => Console.WriteLine("Clicked!");
When the Click() method is called, the event will be triggered.
Lambda expressions offer a concise way to define anonymous methods, making them a natural fit for delegates. Instead of writing separate named method declarations, you can define the logic inline at the point of use, which keeps your code clean and readable.
This is especially useful with built-in delegates like:
To dive deeper into lambda syntax and usage in C#, check out Microsoft's official guide.
Nortal is a strategic innovation and technology company with an unparalleled track-record of delivering successful transformation projects over 20 years.