C# - Anonymous Methods
C# - From Anonymous Methods to Lambda Expressions
Section titled “C# - From Anonymous Methods to Lambda Expressions”In C#, a delegate is an object that references a method. Anonymous methods and their modern successor, lambda expressions, provide a powerful way to create inline, unnamed methods that can be passed as delegate parameters. This is especially useful for event handlers, LINQ queries, and short-lived helper functions.
The Evolution: From Named to Inline Methods
Section titled “The Evolution: From Named to Inline Methods”Let’s see the progression of how you would pass logic to a delegate, starting from a traditional named method and evolving to the concise syntax of a lambda expression.
We’ll use the built-in Action<T> delegate, which represents a method that takes a parameter and returns no value (void).
Step 1: Named Method (Classic Approach)
Section titled “Step 1: Named Method (Classic Approach)”The original way to use a delegate was to assign it a separate, named method that matched its signature.
using System;
// 1. Define a named methodvoid PrintMessage(string message){ Console.WriteLine($"Named Method: {message}");}
// 2. Create a delegate instance pointing to the methodAction<string> printer = PrintMessage;
// 3. Call the delegateprinter("Hello, World!");Anonymous Methods (C# 2.0)
Section titled “Anonymous Methods (C# 2.0)”Introduced in C# 2.0, anonymous methods allowed you to define a method body directly where the delegate instance was created, using the delegate keyword. This avoided the need to create a separate, single-use named method.
Note: While functionally valid, this syntax is now considered historical. Lambda expressions are the preferred modern approach for inline methods.
Syntax and Example
Section titled “Syntax and Example”using System;
// Create a delegate instance using an anonymous methodAction<string> anonymousPrinter = delegate(string msg) { Console.WriteLine($"Anonymous Method: {msg}");};
// Call the delegateanonymousPrinter("This is an anonymous method.");The code block delegate(string msg) { ... } is the anonymous method. The compiler infers the return type from the return statements inside (or the lack thereof for void).
Lambda Expressions (C# 3.0 and beyond)
Section titled “Lambda Expressions (C# 3.0 and beyond)”Lambda expressions, introduced in C# 3.0, provide a much more concise and powerful syntax for creating anonymous functions. They are the cornerstone of modern C# features like LINQ.
A lambda expression is defined with the => (goes to) operator, which separates the input parameters on the left from the expression or statement block on the right.
Syntax and Example
Section titled “Syntax and Example”using System;
// Lambda expression with a statement blockAction<string> lambdaPrinter = (message) =>{ Console.WriteLine($"Lambda Expression: {message}");};
lambdaPrinter("Hello from a lambda!");
// For a single expression, the syntax can be even more concise (expression-bodied lambda)Func<int, int, int> add = (a, b) => a + b;Console.WriteLine($"Sum: {add(5, 10)}"); // Output: Sum: 15Key Advantages of Lambdas:
Section titled “Key Advantages of Lambdas:”- Conciseness: The syntax is minimal and highly readable.
- Type Inference: The compiler can often infer the types of the parameters, so you can write
(a, b) => a + binstead of(int a, int b) => a + b. - Powering LINQ: Lambdas are the language feature that makes LINQ’s fluent query syntax possible.
Practical Example: Using Lambdas with LINQ
Section titled “Practical Example: Using Lambdas with LINQ”The most common use case for lambda expressions today is with LINQ to query collections. The lambda defines the logic for filtering, projecting, or ordering data.
using System;using System.Collections.Generic;using System.Linq;
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Use a lambda expression with the 'Where' method to find all even numbersvar evenNumbers = numbers.Where(n => n % 2 == 0);
Console.WriteLine("Even numbers:");foreach (var num in evenNumbers){ Console.Write($"{num} "); // Output: 2 4 6 8 10}
// Use a lambda with 'Select' to project each number into its squarevar squares = numbers.Select(n => n * n);Console.WriteLine("\n\nSquares:");foreach (var sq in squares){ Console.Write($"{sq} "); // Output: 1 4 9 16 25 36 49 64 81 100}Best Practices and Recommendations
Section titled “Best Practices and Recommendations”- Prefer Lambda Expressions: For any new C# code, you should always use lambda expressions (
=>) instead of the older anonymous method syntax (delegate { ... }). - Use Built-in Delegates: Favor the generic
Action,Func, andPredicatedelegates over defining your own custom delegate types for common patterns. - Keep Lambdas Simple: A lambda should represent a small, focused piece of logic. If it becomes long or complex, refactor it into a private named method for better readability and testability.
- Understand Closures: Be aware that lambdas can “capture” variables from their enclosing scope. This is a powerful feature called a closure, but it can have performance implications or lead to unexpected behavior if not understood properly.