Codectionary / Developer documentation / C#

Delegates

A delegate is a type-safe reference to a method - essentially a variable that can hold a method and be invoked like one. Delegates enable passing behavior around as data, forming the foundation for events, callbacks, and LINQ. C# provides built-in generic delegate types, Func<> and Action<>, that cover the vast majority of use cases without needing to declare a custom delegate type.

Read the syntax

A delegate type describes a callable signature. Assigning a compatible method or lambda produces a delegate value that can be invoked later.

Func has a return value, Action returns void, and multicast delegates invoke their subscribed methods in order. Passing a delegate lets a method accept behavior as data.

Syntax

delegate returnType DelegateName(parameters);

Examples

Declaring and Using a Custom Delegate

Defining a delegate type and assigning a matching method to it.

int Add(int a, int b) => a + b;
int Multiply(int a, int b) => a * b;

MathOperation operation = Add;
Console.WriteLine(operation(3, 4));  // 7

operation = Multiply;
Console.WriteLine(operation(3, 4));  // 12

public delegate int MathOperation(int a, int b);
  1. Defining a delegate type and assigning a matching method to it.
  2. A delegate type describes a callable signature. Assigning a compatible method or lambda produces a delegate value that can be invoked later.
  3. operation first refers to Add and prints 7. Reassigning it to Multiply makes the next call print 12; the call syntax stays the same.

Using Func<> and Action<> Instead

Most of the time, the built-in generic delegates cover what you need without declaring your own type.

Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4));  // 7

Action<string> logMessage = msg => Console.WriteLine($"LOG: {msg}");
logMessage("Application started");
  1. Most of the time, the built-in generic delegates cover what you need without declaring your own type.
  2. A delegate type describes a callable signature. Assigning a compatible method or lambda produces a delegate value that can be invoked later.
  3. Func adds 3 and 4 to print 7. Action writes LOG: Application started and has no return value.

Multicast Delegates

A delegate can reference multiple methods at once, invoking all of them in order when called.

Action<string> notifyEmail = msg => Console.WriteLine($"Email: {msg}");
Action<string> notifySms = msg => Console.WriteLine($"SMS: {msg}");

Action<string> notifyAll = notifyEmail + notifySms;  // combine with +
notifyAll("Order shipped");
// Email: Order shipped
// SMS: Order shipped
  1. A delegate can reference multiple methods at once, invoking all of them in order when called.
  2. A delegate type describes a callable signature. Assigning a compatible method or lambda produces a delegate value that can be invoked later.
  3. Combining two Action delegates creates an invocation list. notifyAll calls email and SMS handlers in order, printing two lines.

Passing a Delegate as a Method Parameter

A very common pattern - accepting behavior as a parameter, similar to how LINQ methods accept lambdas.

void ProcessNumbers(List<int> numbers, Action<int> action)
{
    foreach (int n in numbers)
    {
        action(n);
    }
}

List<int> nums = new List<int> { 1, 2, 3 };
ProcessNumbers(nums, n => Console.WriteLine(n * n));  // 1, 4, 9
  1. A very common pattern - accepting behavior as a parameter, similar to how LINQ methods accept lambdas.
  2. A delegate type describes a callable signature. Assigning a compatible method or lambda produces a delegate value that can be invoked later.
  3. ProcessNumbers invokes the supplied Action for each value. The lambda squares 1, 2 and 3, so it prints 1, 4 and 9.

Best practices

  • Use the built-in Func<>, Action<>, and Predicate<> generic delegates instead of declaring a custom delegate type, unless you need a very specific, named signature
  • Use delegates to pass behavior into a method (like a callback or strategy), rather than duplicating similar methods with slightly different logic
  • Be aware that combining delegates with + creates a multicast delegate that invokes every referenced method in order - useful for notifications, less so for methods with return values (only the last one's result is kept)
  • Prefer events (built on top of delegates) instead of a raw public delegate field when other code should be able to subscribe to, but not directly invoke or reassign, a notification

Runtime & versions

.NET runtime. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

C# language reference

Specifications & further reading