Referencing local variable in anonymous delegates

In C#, anonymous delegates, or lambda expressions, which ultimately is compiled into delegates, are able to access parent context, i.e. accessing variables that is not passed in as an argument. Here I am summarizing the most common cases:

  • Referencing constants
class Program
{
    public void Demo()
    {
        const string message = "Hello, World!";

        var displayMessage = new Action(() => Console.WriteLine(message));

        displayMessage.Invoke();
    }

    private static void Main(string[] args)
    {
        new Program().Demo();
    }
}

In this scenario, the message is defined as constant. After compiler optimization, we can see it is creating a static delegate field in Demo.Program class, linking to a static method b__0. In the static method, the message is directly loaded as a string.

referencing-local-variable-in-anonymous-delegates-1

  • Referencing non const variables
public void Demo()
{
    string message = "Hello, World!";

    var displayMessage = new Action(() => Console.WriteLine(message));

    displayMessage.Invoke();
}

private static void Main(string[] args)
{
    new Program().Demo();
}

It will be a bit complicated when referencing non const local variables. As shown in the screenshot below, a compiler generated class “c__DisplayClass1” is created, and both the “message” variable and delegate method are defined as a non static in this class. As member method can access member fields in the same class, the delegate method naturally has the right to access the message local variable.

referencing-local-variable-in-anonymous-delegates-2

  • Calling a method in the anonymous delegate

Let’s making it more complex by calling a method in the anonymous delegate.

public void Demo()
{
    string message = "Hello, World!";

    var displayMessage = new Action(() => DisplayMessage(message));

    displayMessage.Invoke();
}

private void DisplayMessage(string message)
{
    Console.WriteLine(message);
}

private static void Main(string[] args)
{
    new Program().Demo();
}

You will notice that the generated “c__DisplayClass1” will have a new field named “4__this”, which is of type “Demo.Program”, i.e. the display class is now holding a reference to Demo.Program instance. This is required as in the delegate method, it needs to invoke “DisplayMessage” method defined in “Demo.Program”. While in case 2, since it is not access any member in “Demo.Program”, the generated class won’t hold a reference to “Demo.Program” instance.

referencing-local-variable-in-anonymous-delegates-3

Leave a Reply

Your email address will not be published. Required fields are marked *