Delegates in C# are a unique and powerful feature that lets you store references to methods, enabling flexible, reusable, and event-driven code. They serve as method pointers, allowing you to pass methods as parameters, making your applications highly dynamic and modular. In this tutorial, we'll walk through the types of delegates available in C#, their syntax, use cases, and practical real-world applications.
A delegate
in C# is a type-safe object that points to one or more methods. In simple terms, a delegate acts as a reference to a method, allowing you to encapsulate a method's signature (return type and parameters) and assign any method that matches this signature to the delegate. This is invaluable for creating flexible, callback-style functionality.
To define a delegate, you use the delegate
keyword, followed by a return type and a signature (parameter list). Here’s a simple example:
// Declare a delegate
public delegate void Notify(string message);
class Program
{
static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
static void Main()
{
// Instantiate the delegate
Notify notifier = DisplayMessage;
// Call the delegate
notifier("Hello, Delegate!");
}
}
In this example:
Notify
is a delegate that points to any method matching the void
(string
) signature.DisplayMessage
is assigned to notifier
, allowing you to call DisplayMessage
through notifier
.Delegates can be categorized into single-cast, multi-cast, and generic delegates. Let’s examine each type in more detail.
A single-cast delegate references only one method at a time. It’s straightforward and provides a mechanism to point to a single method:
public delegate void DisplayDelegate(string message);
class Program
{
static void ShowMessage(string message)
{
Console.WriteLine("Message: " + message);
}
static void Main()
{
DisplayDelegate display = ShowMessage;
display("Hello from single-cast delegate!");
}
}
Multi-cast delegates can hold references to more than one method. When invoked, each method in the delegate’s invocation list is called in sequence.
public delegate void Logger();
class Program
{
static void LogToFile()
{
Console.WriteLine("Logging to file...");
}
static void LogToDatabase()
{
Console.WriteLine("Logging to database...");
}
static void Main()
{
Logger logger = LogToFile;
logger += LogToDatabase;
logger(); // Calls both LogToFile and LogToDatabase
}
}
Use Case: Multi-cast delegates are beneficial in event handling, where multiple methods need to respond to a single event.
C# provides generic delegates Func
, Action
, and Predicate
for flexible, reusable delegate types:
void
).class Program
{
static void Main()
{
Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(2, 3)); // Outputs 5
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Alice");
Predicate<int> isPositive = num => num > 0;
Console.WriteLine(isPositive(5)); // Outputs True
}
}
Use Case: Generic delegates are ideal for passing methods without declaring custom delegate types.
Let’s create a real-world scenario where we want to implement a logger that can log messages to different outputs, such as console, file, and a database. With delegates, we can define flexible logging mechanisms.
using System;
using System.Collections.Generic;
public delegate void Logger(string message);
class LoggerSystem
{
public static void LogToConsole(string message)
{
Console.WriteLine("Console Log: " + message);
}
public static void LogToFile(string message)
{
Console.WriteLine("File Log: " + message);
}
public static void LogToDatabase(string message)
{
Console.WriteLine("Database Log: " + message);
}
}
class Program
{
static void Main()
{
Logger logger = LoggerSystem.LogToConsole;
logger += LoggerSystem.LogToFile;
logger += LoggerSystem.LogToDatabase;
logger("This is a multi-cast delegate log message");
}
}
Logger
: A delegate
type that matches any method taking a single string
parameter.Real-World Use Case: In logging systems, multi-cast delegates are advantageous for scenarios where logs need to be sent to multiple outputs at once, enabling an extensible, pluggable architecture.
Func
, Action
, and Predicate
generic delegates offer reusable solutions for standard method signatures.Delegates are an essential feature of C#, providing a flexible mechanism for referencing methods. They allow developers to implement callbacks, event handling, and multi-casting, supporting dynamic and modular programming. By using different types of delegates—single-cast, multi-cast, and generic—you can handle various use cases, from simple method references to complex, multi-method event handling.