BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

C# Programming
.NET 3.5+

C# Closures

Closures are usually associated with functional programming languages, where they link a function to its referencing environment, permitting access to variables outside of the function's scope. With the use of delegates, closures are available in C#.

Closures Capture Variables, Not Values

Some programming languages capture the values of variables used in closures when those closures are defined. C# captures the variables themselves. This is an important distinction as it can lead to values changing over the scope boundary. To illustrate, consider the following code. Here we are creating a closure that outputs our familiar mathematical equation. When the delegate is declared, the value of the integer variable is 1. However, after the closure is declared, but before it is executed, the variable's value changes to 10.

int nonLocal = 1;
Action closure = delegate
{
    Console.WriteLine("{0} + 1 = {1}", nonLocal, nonLocal + 1);
};

nonLocal = 10;

closure();

You might expect that, as the non-local variable has a value of one at the time the closure is created, the resultant output would be "1 + 1 = 2". Indeed, with some programming languages this is what would happen. However, because it is the variable that is captured, the change to its value does impact the execution of the closure. The actual output is:

10 + 1 = 11

Changes to a non-local closure variable are transmitted in the other direction too. In the following code the delegate changes the value before the declaring code shows it. The change is visible in the external code, despite it being in a different scope to that of the closure.

int nonLocal = 1;
Action closure = delegate
{
    nonLocal++;
};
closure();

Console.WriteLine(nonLocal);    // 2

The variable capture leads us to the potential of introducing unexpected bugs to our code. We can demonstrate the problem with another example. This time we are using closures in a common scenario: multi-threaded or parallel programming. The code below shows a for loop that creates and starts five new threads. Each pauses briefly before showing the value from the loop's control variable. If the value of the control variable were captured, we'd see the numbers from one to five written to the console, though perhaps not in the correct order. However, as it is the variable that is bound to the closure, and the loop completes before the threads output their messages, we actually see the final value of 6 for each thread.

for (int i = 1; i <= 5; i++)
{
    new Thread(delegate()
    {
        Thread.Sleep(100);
        Console.Write(i);
    }).Start();
}

// Outputs "66666"

Luckily, this type of problem is easily fixed when you understand that it is variables and not values that are captured. All we need to do is to introduce a new variable instance for each iteration of the loop. This can be declared in the loop's body and given the value from the control variable. Under normal circumstances the temporary variable would go out of scope when the loop terminates but the closure will bind to it and keep it alive.

In the code below five instances of the "value" variable are created and given five different values, each bound to a different thread.

for (int i = 1; i <= 5; i++)
{
    int value = i;
    new Thread(delegate()
    {
        Thread.Sleep(100);
        Console.Write(value);
    }).Start();
}

// Outputs "12345"

NB: The output may vary depending upon the order in which the threads execute.

24 July 2012