C# - Delegates
C# - Delegates and Lambda Expressions
Section titled “C# - Delegates and Lambda Expressions”A delegate in C# is a type that safely encapsulates a method, similar to a function pointer in C++. It acts as a reference to a method with a specific parameter list and return type. Delegates are the foundation for events, callbacks, and LINQ.
While you can declare custom delegate types, modern C# development heavily relies on the built-in generic delegates (Func, Action, Predicate) and lambda expressions for a more concise and powerful syntax.
The Modern Approach: Generic Delegates
Section titled “The Modern Approach: Generic Delegates”The .NET library provides built-in generic delegate types that cover most common scenarios, removing the need to declare custom ones.
| Delegate | Description | Example |
|---|---|---|
Func<T, TResult> | Represents a method that takes one or more input parameters (T) and returns a value (TResult). There are overloads for up to 16 input parameters. | Func<int, int, int> add; // Takes two ints, returns an int. |
Action<T> | Represents a method that takes one or more input parameters (T) but does not return a value (void). | Action<string> print; // Takes a string, returns void. |
Predicate<T> | A specialized version of Func that takes one parameter (T) and always returns a bool. It is typically used for testing a condition. | Predicate<int> isEven; // Takes an int, returns a bool. |
Lambda Expressions: The Power of Conciseness
Section titled “Lambda Expressions: The Power of Conciseness”A lambda expression is an anonymous function that you can use to create delegates or expression tree types. It provides a shorthand way to write methods without needing a full method declaration.
// Lambda Syntax: (input-parameters) => { expression-or-statement-block }
// Example 1: A Func<> that adds two numbersFunc<int, int, int> add = (x, y) => x + y;int sum = add(10, 5);Console.WriteLine($"Sum: {sum}"); // Output: Sum: 15
// Example 2: An Action<> that prints a messageAction<string> greet = name => Console.WriteLine($"Hello, {name}!");greet("World"); // Output: Hello, World!
// Example 3: A Predicate<> that checks if a number is positivePredicate<int> isPositive = n => n > 0;Console.WriteLine($"Is -5 positive? {isPositive(-5)}"); // Output: Is -5 positive? FalseMulticast Delegates
Section titled “Multicast Delegates”Delegates have a powerful feature called multicasting, where a single delegate can reference and invoke multiple methods. You can use the + and - (or += and -=) operators to add or remove methods from a delegate’s invocation list.
Action<string> logger;
Action<string> logToConsole = message => Console.WriteLine($"CONSOLE: {message}");Action<string> logToFile = message => System.IO.File.AppendAllText("log.txt", $"{message}\n");
// Combine two methods into one delegate instancelogger = logToConsole;logger += logToFile;
// Invoking 'logger' will now call both methodslogger("System startup complete.");
// Remove a methodlogger -= logToConsole;logger("User logged in."); // This will only be written to the file
Console.WriteLine("Logging complete. Check log.txt");When you run this, “System startup complete.” is printed to the console and the log file. “User logged in.” is only written to the log file.
Key Practical Applications of Delegates
Section titled “Key Practical Applications of Delegates”1. LINQ (Language Integrated Query)
Section titled “1. LINQ (Language Integrated Query)”Delegates are the backbone of LINQ. Methods like Where, Select, and OrderBy all take delegates (usually as lambda expressions) to perform their operations.
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 'Where' takes a Func<int, bool> (or Predicate<int>)var evenNumbers = numbers.Where(n => n % 2 == 0);
// 'Select' takes a Func<int, TResult>var squaredNumbers = evenNumbers.Select(n => n * n);
Console.WriteLine(string.Join(", ", squaredNumbers)); // Output: 4, 16, 36, 64, 1002. Event Handling
Section titled “2. Event Handling”Events in C# use delegates to define the signature for event handlers. When an event is raised, all registered handler methods are invoked.
public class Button{ // The event is based on the Action delegate public event Action? Clicked;
public void SimulateClick() { Console.WriteLine("Button was clicked!"); Clicked?.Invoke(); // Safely invoke the delegate }}
var myButton = new Button();
// Subscribe to the event using a lambda expressionmyButton.Clicked += () => Console.WriteLine("Event handler: Responding to click!");myButton.Clicked += () => Console.WriteLine("Another handler: Updating UI...");
myButton.SimulateClick();When the above code is compiled and executed, it produces the following result:
Button was clicked!Event handler: Responding to click!Another handler: Updating UI...