How to use closures in C#

By FoxLearn 1/5/2025 10:12:34 AM   55
Closures in C# enabled through anonymous methods, lambda expressions, and delegates improve code robustness, efficiency, readability, and maintainability by allowing functions to access non-local variables.

Delegates, which are type-safe function pointers matching a specific signature, are used for callbacks and event handling.

Closures as First-Class Functions in C#

A closure in C# is a first-class function tied to its lexical environment, allowing it to access free variables from that environment. C# treats functions like first-class data types, enabling them to be assigned, invoked, or passed around like other data types.

Closure Examples in C#

You can write a closure using an anonymous method as shown below:

Func<int, int> multiplyByTwo = delegate (int number)
{
    return number * 2;
};

Alternatively, you can create a closure using a lambda expression:

Func<int, int> multiplyByTwo = number => number * 2;

Both snippets create a method that accepts an integer as a parameter and returns its double.

Here’s how you can invoke these closures:

int result = multiplyByTwo(5); // Output: 10

Now, let’s explore closures capturing non-local variables.

int y = 20;
Action displayValue = delegate
{
    Console.WriteLine($"The value of the non-local variable y is: {y}");
};
displayValue();

The same can be achieved using a lambda expression:

int y = 20;
Action displayValue = () =>
{
    Console.WriteLine($"The value of the non-local variable y is: {y}");
};
displayValue();

In both cases, the output will be:

The value of the non-local variable y is: 20

Closures Capture Variables, Not Values

Closures in C# are bound to the variables in their lexical environment, not just their values.

int z = 30;
Action showValue = delegate { Console.WriteLine($"The value of z is: {z}"); };
z = 50;
showValue();

When executed, the output will be:

The value of z is: 50

This behavior occurs because the closure references the variable z itself, not its original value.

How Do C# Closures Work?

When the C# compiler encounters a closure that captures local variables beyond its original scope, it promotes the delegate and its associated variables into a compiler-generated class. This ensures the variables remain accessible even if their original scope ends.

[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public int z;
    internal void <M>b__0()
    {
        Console.WriteLine($"The value of z is: {z}");
    }
}

This mechanism allows closures to keep variables alive as long as the closure itself is callable. Originally from functional programming, closures now play a significant role in object-oriented languages like C#, simplifying the implementation of delegates.