Why declare a local function static in C# 8.0

6.9k Views Asked by At

In C# 8.0, Static Local Functions are announced

Can anyone help enlighten me as to why you would want to declare a local function as static?

The reason given in in the article:

to ensure that local function doesn't capture (reference) any variables from the enclosing scope

But:

  1. I don't understand why would you want to ensure that?
  2. Is there any other reason or benefits to declare it static? (performance maybe?)

The example code given in the article is:

int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right;
}
4

There are 4 best solutions below

2
On BEST ANSWER

This answer from CodeCaster and this separate answer from György Kőszeg individually answer different parts of my question, so I'm bringing them both together to form the full picture for the accepted answer:

For Part 1) of my question, @CodeCaster Says:

Because it prevents you from shooting yourself in the foot. It forces the local function to be a pure function that does not modify the state of the caller.

in larger codebases maintained by multiple people and not properly covered by unit tests, this simple modifier prevents grief

So Answer 1 is: Static Local Functions ensure reliable caller method state.

For Part 2) of my question, @György Kőszeg Says:

Capturing variables has a small additional cost as it will generate an internally used type where your captured variables are public fields

And he goes on to give an example of the produced compiler code via reflector.

So Answer 2 is: Static Local Functions prevent variable capturing. Variable capturing comes at a small cost. So there is a small performance boost by declaring the local function static

0
On

I think this is just to ensure correct usage of the variables used in the local function, as the documentation says. In large and complex methods, it can prevent accidental usage of enclosing scope variables if there are variables with the same name in the local function.

1
On

I don't understand why would you want to ensure that?

Because it prevents you from shooting yourself in the foot. It forces the local function to be a pure function that does not modify the state of the caller.

This returns false, because the function modifies local variables of its caller:

public bool Is42()
{
    int i = 42;     
    Foo();      
    return i == 42;

    void Foo()
    {
        i = 21;
    }   
}

And this doesn't, because it doesn't even compile:

public bool Is42()
{
    int i = 42;     
    Foo();      
    return i == 42;

    static void Foo()
    {
        i = 21;
    }   
}

It prevents surprises. Of course in these simple examples the benefit isn't immediately clear, because "well it's obvious that Foo() modifies i", but in larger codebases maintained by multiple people and not properly covered by unit tests, this simple modifier prevents grief.

6
On

Capturing variables has a small additional cost as it will generate an internally used type where your captured variables are public fields. Consider a slightly modified example:

int M()
{
    int y = 5;
    int x = 7;
    return Add();

    int Add() => x + y;
}

It will actually translate to something like this:

int M()
{
    int y = 5;
    int x = 7;
    var capturedVars = new <>c__DisplayClass0_0 { x = x, y = y };
    return <M>g__Add|0_0(ref capturedVars);
}

[CompilerGenerated]
private struct <>c__DisplayClass0_0
{
    public int x;
    public int y;
}

[CompilerGenerated]
internal static int <M>g__Add|0_0(ref <>c__DisplayClass0_0 class_Ref1) => 
    (class_Ref1.x + class_Ref1.y);