← C# EnglishChapter 12 of 13

Delegates, Events, and Lambdas

## Learning Objectives - Understand delegates - Work with events - Master lambda expressions - Learn anonymous methods ## Delegates ### What is a Delegate? A delegate is a type that represents references to methods with a specific signature. ```csharp // Delegate declaration delegate int Calculate(int a, int b); // Methods matching the signature class MathOperations { public static int Add(int a, int b) => a + b; public static int Multiply(int a, int b) => a * b; } // Usage Calculate calc = MathOperations.Add; int result = calc(5, 3); // 8 calc = MathOperations.Multiply; result = calc(5, 3); // 15 ``` ### Generic Delegates (Built-in) ```csharp // Func - returns a value Func add = (a, b) => a + b; Func double = x => x * 2; Func getName = () => "Alice"; // Action - returns void Action print = Console.WriteLine; Action multiply = (a, b) => Console.WriteLine(a * b); // Predicate - returns bool Predicate isEven = x => x % 2 == 0; ``` ## Events ### Publish-Subscribe Pattern ```csharp class Button { // Public event public event EventHandler? Clicked; public void Click() { Console.WriteLine("Button clicked"); // Raise event if there are subscribers Clicked?.Invoke(this, EventArgs.Empty); } } // Subscriber class Program { static void Main() { Button button = new Button(); // Subscribe to event button.Clicked += OnButtonClicked; button.Click(); } static void OnButtonClicked(object? sender, EventArgs e) { Console.WriteLine("Button was clicked!"); } } ``` ### EventHandler ### Custom Event Args ```csharp class TemperatureEventArgs : EventArgs { public double Temperature { get; } public TemperatureEventArgs(double temp) => Temperature = temp; } class Thermometer { public event EventHandler? TemperatureChanged; public void Measure(double temp) { if (temp != lastTemp) { lastTemp = temp; TemperatureChanged?.Invoke(this, new TemperatureEventArgs(temp)); } } } // Usage Thermometer t = new Thermometer(); t.TemperatureChanged += (sender, e) => Console.WriteLine($"Temp: {e.Temperature}"); ``` ## Lambda Expressions ### Syntax ```csharp // Full syntax (int x, int y) => { return x + y; } // Expression body (no return, no braces) x => x * 2; // Multiple parameters (a, b) => a + b; // No parameters () => Console.WriteLine("Hello"); ``` ### Examples ```csharp // With Func Func square = x => x * x; Func concat = (a, b) => a + b; // With Action Action print = msg => Console.WriteLine(msg); // With Predicate Predicate isPositive = x => x > 0; // Complex logic Func grade = score => { if (score >= 90) return "A"; if (score >= 80) return "B"; return "C"; }; ``` ## Anonymous Methods ### Before Lambdas ```csharp // Anonymous method delegate int Calculate(int a, int b); Calculate calc = delegate(int a, int b) { return a + b; }; // With events button.Clicked += delegate(object? sender, EventArgs e) { Console.WriteLine("Clicked!"); }; ``` ## Closures ### Capturing Variables ```csharp int multiplier = 10; Func multiply = x => x * multiplier; Console.WriteLine(multiply(5)); // 50 multiplier = 20; Console.WriteLine(multiply(5)); // 200 (captures by reference!) ``` ### Common Pitfall ```csharp var actions = new List(); // Wrong - all capture same variable for (int i = 0; i < 3; i++) { actions.Add(() => Console.WriteLine(i)); } foreach (var a in actions) a(); // Prints: 3, 3, 3 // Correct - capture value var actions2 = new List(); for (int i = 0; i < 3; i++) { int captured = i; // Capture value actions2.Add(() => Console.WriteLine(captured)); } foreach (var a in actions2) a(); // Prints: 0, 1, 2 ``` ## Delegates Combining (Multicast) ### Combining Delegates ```csharp delegate void Notify(); Notify notify1 = () => Console.WriteLine("Handler 1"); Notify notify2 = () => Console.WriteLine("Handler 2"); Notify combined = notify1 + notify2; combined(); // Calls both Notify notify3 = () => Console.WriteLine("Handler 3"); combined += notify3; combined(); // Calls all three // Remove combined -= notify2; combined(); // Handler 1 and 3 ``` ### Return Values with Multicast ```csharp delegate int Calculate(int x); Calculate calc = x => { Console.WriteLine($"First: {x}"); return x + 1; }; calc += x => { Console.WriteLine($"Second: {x}"); return x + 2; }; int result = calc(5); // Only last return value is kept! // Output: // First: 5 // Second: 5 // result = 7 ``` ## Built-in Event Patterns ### Standard Event Signature ```csharp // Standard delegate types (already defined in .NET) public delegate void EventHandler(object? sender, EventArgs e); public delegate void EventHandler(object? sender, TEventArgs e); // Common pattern public event EventHandler? MyEvent; public event EventHandler? SpecializedEvent; ``` ## Covariance and Contravariance ### With Delegates ```csharp // Covariance - return type can be more derived delegate TextWriter WriterFactory(); WriterFactory factory = () => new StreamWriter("file.txt"); // Contravariance - parameter type can be less derived Action writer = (StreamWriter sw) => sw.WriteLine("test"); Action textWriter = writer; // OK - TextWriter is base of StreamWriter ``` ## Summary - Delegate: type-safe method reference - Func: returns value - Action: returns void - Predicate: returns bool - Event: publisher-subscriber pattern - Lambda: concise anonymous methods - Closures capture variables by reference - Delegates can be combined (+, -) - Events follow standard .NET pattern

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →