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:
// 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:
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:
- We define a
MathOperation
delegate type - We create two methods (
Add
andSubtract
) that match the delegate's signature - We instantiate delegate objects that point to these methods
- We invoke the delegates just like we would call a method
Delegate Instantiation Syntax
There are multiple ways to instantiate delegates in C#:
// 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:
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:
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
Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Hello, delegates!");
Func<T, TResult>
- Represents a method that returns a value of type TResult and takes parameters of type T
Func<int, int, int> multiply = (x, y) => x * y;
int result = multiply(5, 4); // result = 20
Predicate<T>
- Represents a method that takes a parameter of type T and returns a boolean
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
-
Create a delegate that takes a string and returns an integer, then write methods to count characters or words in a string.
-
Implement a basic calculator using delegates for different operations (add, subtract, multiply, divide).
-
Create a method that sorts an array and uses a delegate to determine the sorting criteria.
-
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! :)