Skip to main content

C# Delegates Basics

Introduction

Delegates are an essential feature in C# that enables flexible and powerful programming patterns. A delegate in C# is a type that represents references to methods with a specific parameter list and return type. You can think of delegates as type-safe function pointers or a way to treat methods as first-class objects.

In this tutorial, we'll explore the fundamentals of C# delegates, including how to define, instantiate, and use them in your applications.

What Are Delegates?

A delegate is essentially a reference type that can hold a reference to a method. Delegates are particularly useful for implementing:

  • Callback mechanisms
  • Event handling
  • Method passing as parameters
  • Creating flexible and pluggable architectures

Defining a Delegate

To define a delegate, you use the delegate keyword followed by a return type and a parameter list, similar to how you would define a method signature:

csharp
// Define a delegate that takes an int and returns an int
public delegate int MathOperation(int x, int y);

This delegate, MathOperation, can reference any method that:

  • Takes two integer parameters
  • Returns an integer result

Creating and Using Delegates

Let's see how to create and use delegates with a simple example:

csharp
using System;

namespace DelegatesBasics
{
// Define the delegate
public delegate int MathOperation(int x, int y);

class Program
{
// Methods matching the delegate signature
static int Add(int x, int y)
{
return x + y;
}

static int Subtract(int x, int y)
{
return x - y;
}

static void Main(string[] args)
{
// Create delegate instances
MathOperation addOperation = Add;
MathOperation subtractOperation = Subtract;

// Invoke the delegates
int sum = addOperation(10, 5);
int difference = subtractOperation(10, 5);

Console.WriteLine($"Sum: {sum}");
Console.WriteLine($"Difference: {difference}");
}
}
}

Output:

Sum: 15
Difference: 5

In this example:

  1. We define a MathOperation delegate type
  2. We create two methods (Add and Subtract) that match the delegate's signature
  3. We instantiate delegate objects that point to these methods
  4. We invoke the delegates just like we would call a method

Delegate Instantiation Syntax

There are multiple ways to instantiate delegates in C#:

csharp
// Traditional way
MathOperation addOperation = new MathOperation(Add);

// Simplified syntax (C# 2.0 and later)
MathOperation addOperation = Add;

// Using anonymous methods (C# 2.0 and later)
MathOperation multiplyOperation = delegate(int x, int y) {
return x * y;
};

// Using lambda expressions (C# 3.0 and later)
MathOperation divideOperation = (x, y) => x / y;

Multicast Delegates

One powerful feature of delegates is their ability to hold references to multiple methods. These are called multicast delegates. You can add or remove methods using the += and -= operators:

csharp
using System;

namespace MulticastDelegates
{
public delegate void Notification(string message);

class Program
{
static void EmailNotification(string message)
{
Console.WriteLine($"Sending email: {message}");
}

static void SMSNotification(string message)
{
Console.WriteLine($"Sending SMS: {message}");
}

static void PushNotification(string message)
{
Console.WriteLine($"Sending push notification: {message}");
}

static void Main(string[] args)
{
// Create a multicast delegate
Notification notificationSystem = EmailNotification;

// Add more methods to the invocation list
notificationSystem += SMSNotification;
notificationSystem += PushNotification;

// Calling the delegate will execute all methods
Console.WriteLine("Sending notifications to all channels:");
notificationSystem("System maintenance at 10 PM");

// Remove a method
notificationSystem -= SMSNotification;

Console.WriteLine("\nAfter removing SMS notification:");
notificationSystem("Maintenance completed");
}
}
}

Output:

Sending notifications to all channels:
Sending email: System maintenance at 10 PM
Sending SMS: System maintenance at 10 PM
Sending push notification: System maintenance at 10 PM

After removing SMS notification:
Sending email: Maintenance completed
Sending push notification: Maintenance completed

Real-World Example: Callback Pattern

Delegates are commonly used to implement callbacks. Here's a practical example showing how a method can report progress using a delegate callback:

csharp
using System;
using System.Threading;

namespace DelegateCallback
{
// Define a delegate for progress reporting
public delegate void ProgressReporter(int percentComplete);

class FileProcessor
{
// This method accepts a delegate as a parameter
public void ProcessFile(string filePath, ProgressReporter progressCallback)
{
Console.WriteLine($"Starting to process file: {filePath}");

// Simulate file processing with progress updates
for (int i = 0; i <= 100; i += 20)
{
// Simulate work
Thread.Sleep(500);

// Report progress via callback
progressCallback(i);
}

Console.WriteLine("File processing completed!");
}
}

class Program
{
// This method will be used as a callback
static void ShowProgress(int percentComplete)
{
Console.WriteLine($"Processing: {percentComplete}% complete");
}

static void Main(string[] args)
{
FileProcessor processor = new FileProcessor();

// Pass the ShowProgress method as a callback
processor.ProcessFile("sample.txt", ShowProgress);

// We can also use a lambda expression directly
Console.WriteLine("\nProcessing another file...");
processor.ProcessFile("another.txt",
(percent) => Console.WriteLine($"File processing: {percent}%"));
}
}
}

Output:

Starting to process file: sample.txt
Processing: 0% complete
Processing: 20% complete
Processing: 40% complete
Processing: 60% complete
Processing: 80% complete
Processing: 100% complete
File processing completed!

Processing another file...
Starting to process file: another.txt
File processing: 0%
File processing: 20%
File processing: 40%
File processing: 60%
File processing: 80%
File processing: 100%
File processing completed!

Generic Delegates in .NET

The .NET Framework provides several predefined generic delegate types:

** - Represents a method that doesn't return a value (returns void) and takes parameters of type T

csharp
Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Hello, delegates!");
  1. Func<T, TResult> - Represents a method that returns a value of type TResult and takes parameters of type T
csharp
Func<int, int, int> multiply = (x, y) => x * y;
int result = multiply(5, 4); // result = 20
  1. Predicate<T> - Represents a method that takes a parameter of type T and returns a boolean
csharp
Predicate<int> isPositive = x => x > 0;
bool check = isPositive(10); // check = true

Using these predefined delegates often eliminates the need to define custom delegate types.

Summary

Delegates are a powerful feature in C# that enable:

  • Passing methods as parameters
  • Implementing callbacks
  • Building event-driven applications
  • Creating flexible and reusable code

Key concepts covered:

  • Defining delegate types
  • Instantiating delegates
  • Invoking delegates
  • Multicast delegates
  • Using delegates for callbacks
  • Predefined generic delegates

Understanding delegates is essential for C# development and forms the foundation for events, which we'll cover in the next tutorial.

Practice Exercises

  1. Create a delegate that takes a string and returns an integer, then write methods to count characters or words in a string.

  2. Implement a basic calculator using delegates for different operations (add, subtract, multiply, divide).

  3. Create a method that sorts an array and uses a delegate to determine the sorting criteria.

  4. Implement a simple event system using multicast delegates where multiple subscribers can register for notifications.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)