SoFunction
Updated on 2025-03-04

Detailed explanation of the use of closures in C#

Preface

In C#, closures are a powerful concept that allows functions to capture external variables and access them outside of functions.

Closures are very useful in many scenarios, such as in anonymous functions, Lambda expressions, and delegates. This tutorial will introduce closures in C# in detail.

The basic concept of closure

Closure is a technique that encapsulates a function with its surrounding environment (i.e., external variables).

In C#, closures are usually implemented through anonymous functions or Lambda expressions.

When an anonymous function or Lambda expression refers to an external variable, the variable is "captured" into the closure and can be accessed and modified inside the function.

Closures in anonymous functions

1. Define and use anonymous functions

delegate int AnonFunc();
AnonFunc func = delegate()
{
    return 10;
};

Anonymous functions are functions without names that can be defined and used directly in the code.

In C#, anonymous functions are usually defined using the delegate keyword or Lambda expression.

For example:

  • In this example, we define an anonymous function and assign it to a delegate variable.
  • This anonymous function has no parameters and returns a value of 10.

2. Anonymous functions capture external variables

Anonymous functions can capture external variables and access and modify them inside the function.

For example:

int x = 5;
delegate int AnonFunc();
AnonFunc func = delegate()
{
    return x;
};

In this example, the anonymous function captures the external variable x and returns the value of the variable inside the function.

3. The life cycle of closures

delegate int AnonFunc();
int x = 5;
AnonFunc func = delegate()
{
    return x;
};
x = 10;
(func());

The life cycle of a closure is the same as the life cycle of a delegate or Lambda expression that captures it.

This means that as long as a delegate or Lambda expression exists, the closure exists and the captured variables can be accessed and modified.

For example:

  • In this example, we first define an anonymous function that captures external variablesx
  • Then, we modified the variablexThe value of , and the anonymous function is called.
  • Due to the existence of the closure, the anonymous function returns the modified variablexvalue.

Closure in Lambda expression

1. Define and use Lambda expressions

Lambda expressions are a concise and anonymous function syntax that can be defined and used directly in the code.

In C#, Lambda expressions are usually used=>operators to define.

For example:

Func<int> func = () => 10;

In this example, we define a Lambda expression and assign it to a delegate variable.

This Lambda expression has no arguments and returns a value of 10.

2. Lambda expression captures external variables

int x = 5;
Func<int> func = () => x;

Lambda expressions can capture external variables and access and modify them inside a function.

For example:

In this example, the Lambda expression captures the external variablex, and the value of this variable is returned inside the function.

3. The scope of closure

The scope of a variable in a closure is the same as the scope of the Lambda expression that captures it.

This means that as long as the Lambda expression exists, the closure exists and the captured variables can be accessed and modified.

For example:

int x = 5;
 Func<int> func = () => x;
 x = 10;
 (func());

In this example:

  • We first define a Lambda expression that captures the external variable x.
  • We then modified the value of variable x and called the Lambda expression.
  • Due to the existence of the closure, the Lambda expression returns the value of the modified variable x.

Application scenarios of closures

1. Event handling

Button button = new Button();
int count = 0;
 += (sender, e) =>
{
    count++;
    ($"Button clicked {count} times.");
};

Closures are very useful in event processing because they capture context information when an event occurs.

For example, in a WPF or WinForms application, we can use closures to handle button click events and access button properties or other context information.

For example:

  • In this example, we use closures to handle button click events.
  • Closure captures external variablescount, and increase the value of this variable every time the button is clicked, and outputs the number of clicks on the console.

2. Asynchronous programming

Closures are also very useful in asynchronous programming because they capture context information when an asynchronous operation occurs.

For example, in useasyncandawaitWhen keywords are asynchronously programmed, we can use closures to access the results of asynchronous operations or other context information.

For example:

   async Task<int> GetDataAsync()
   {
       await (1000);
       return 10;
   }

   int x = 5;
   Func<int> func = async () =>
   {
       int data = await GetDataAsync();
       return x + data;
   };

   int result = await func();
   (result);

In this example:

  • We use closures to access the results of asynchronous operations.
  • The closure captures the external variable x, and adds it to the result of the asynchronous operation after it is completed, and returns the result.

3. Iterator and LINQ query

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
int sum = 0;
foreach (int number in numbers)
{
    sum += number;
}
(sum);

Closures are also very useful in iterators and LINQ queries because they can capture the context information of an iterator or query.

For example, when using a foreach loop or LINQ query, we can use closures to access the current element or other context information of the iterator or query.

For example:

In this example:

  • We useforeachLoop to iterate over a list of integers and accumulate each element into a variable.
  • Inside the loop, we use closures to capture external variablessum, and accumulate the current element into this variable at each iteration.

Notes on closures

1. Side effects of variable capture

Closures capture external variables can cause some unexpected side effects.

For example, if the captured variable is a reference type and the value of the variable is modified inside the closure, the modification may affect references to the variable elsewhere.

For example:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
 Func<List<int>> func = () => numbers;
 (6);
 (func().Count);

In this example:

  • We define a closure that captures the external variable numbers.
  • We then modified the value of this variable outside the closure and called the closure.
  • Since the closure captures external variables, the list returned by the closure also contains the modified elements.

2. The performance impact of closures

Closures may have some performance impact, as they need to capture external variables and allocate memory on the heap.

In some performance-sensitive scenarios, we may need to consider avoiding closures or using other technologies to replace closures.

For example, in some high-performance computing scenarios, we can use structures instead of classes to avoid the performance overhead of closures.

3. Memory management of closures

Closures can cause memory leaks because they may capture external variables and keep references to those variables.

In some long-running applications, we need to pay attention to the memory management of closures to avoid unnecessary memory leaks.

For example, when using event processing, we need to be careful to unsubscribe from events when event processing is no longer needed to avoid memory leaks in closures.

Summarize

Closures are a powerful concept in C# that allows functions to capture external variables and access them outside of functions.

Closures are very useful in many scenarios, such as in anonymous functions, Lambda expressions, and delegates.

When using closures, we need to pay attention to the side effects of variable capture, performance impact, and memory management issues to ensure the correctness and performance of the code.

The above is personal experience. I hope you can give you a reference and I hope you can support me more.