Why is a local function not always hidden in C#7?

c# local functions
c# local functions best practices
c# local function performance
c# async local function
vb net local function
c# local function vs private function
local methods
c# private function

What I am showing below, is rather a theoretical question. But I am interested in how the new C#7 compiler works and resolves local functions.

In C#7 I can use local functions. For example (you can try these examples in LinqPad beta):

Example 1: Nested Main()

void Main()
{
    void Main()
    {
        Console.WriteLine("Hello!");
    }
    Main();
}

DotNetFiddle for Example 1

Rather than calling Main() in a recursive way, the local function Main() is being called once, so the output of this is:

Hello!

The compiler accepts this without warnings and errors.

Example 2: Here, I am going one level deeper, like:

DotNetFiddle for Example 2

In this case, I would also expect the same output, because the innermost local function is called, then one level up, Main() is just another local function with a local scope, so it should be not much different from the first example.

But here, to my surprise, I am getting an error:

CS0136 A local or parameter named 'Main' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter


Question: Can you explain why this error happens in Example 2, but not in Example 1?

I thought, each inner Main() would have a local scope and is hidden outside.


Update: Thank you to all who have made contibutions so far (either answers or comments), it is very worthwile what you wrote to understand the behavior of the C# compiler.

From what I read, and after considering the possibilities, what I found out with your help is that it can be either a compiler bug or behavior by design.

Recall that C# had some design goals which differentiate it from languages like C++.

If you're interested what I have done to investigate it further: I have renamed the innermost function to MainL like:

Example 2b:

void Main()
{
    void Main()
    {
        void MainL()
        {
            Console.WriteLine("Hello!");
        }
        MainL();
    }
    Main();     
}

This modified example compiles and runs successfully.

Now when you compile this with LinqPad and then switch to the IL tab you can see what the compiler did:

It created the innermost MainL function as g__MainL0_1, the enclosing Main function has the label g__Main0_0.

That means, if you remove the L from MainL you will notice that the compiler already renames it in a unique way, because then the code looks like:

IL_0000:  call        UserQuery.<Main>g__Main0_0
IL_0005:  ret         

<Main>g__Main0_0:
IL_0000:  call        UserQuery.<Main>g__Main0_1
IL_0005:  ret         

<Main>g__Main0_1:
IL_0000:  ldstr       "Hello!"
IL_0005:  call        System.Console.WriteLine
IL_000A:  ret         

which would still resolve correctly. Since the code doesn't look like this in Example 2, because the compiler stops with an error, I do now assume that the behavior is by design, it is not likely a compiler bug.

Conclusion: Some of you wrote that in C++ recursive resolution of local functions can lead to refactoring issues, and others wrote that this kind of behavior in C# is what the compiler does with local variables (note that the error message is the same) - all that even confirms me thinking it was done like this by design and is no bug.

Parameters and local variables from the enclosing scope are available inside a local function.

I thought, each inner Main() would have a local scope and is hidden outside.

C# does not overwrite names from the parent scope, so there is and ambiguity for the local name Main which defined in current and parent scopes.

So in the second example both declaration of void Main() are available for the inner scope and compiler shows you an error.

Here is an example with variables and local functions which could help you to see the problem in the familiar environment. To make it clear that it is only matter of scope I modified example and added functions to variables to make it clear:

class Test
{
    int MainVar = 0;
    public void Main()
    {
        if (this.MainVar++ > 10) return;
        int MainVar = 10;
        Console.WriteLine($"Instance Main, this.MainVar=${this.MainVar}, MainVar={MainVar}");
        void Main()
        {
            if (MainVar++ > 14) return;
            Console.WriteLine($"Local Main, this.MainVar=${this.MainVar}, MainVar={MainVar}");
            // Here is a recursion you were looking for, in Example 1
            this.Main();
            // Let's try some errors!
            int MainVar = 110; /* Error! Local MainVar is already declared in a parent scope. 
                //  Error CS0136  A local or parameter named 'MainVar' cannot be declared in this scope 
                // because that name is used in an enclosing local scope to define a local or parameter */
            void Main() { } /* Error! The same problem with Main available on the parent scope. 
                // Error CS0136  A local or parameter named 'Main' cannot be declared in this scope 
                // because that name is used in an enclosing local scope to define a local or parameter */
        }
        Main(); // Local Main()
        this.Main(); // Instance Main()
        // You can have another instance method with a different parameters
        this.Main(99);
        // But you can't have a local function with the same name and parameters do not matter
        void Main(int y) { } // Error! Error CS0128  A local variable or function named 'Main' is already defined in this scope
    }
    void Main(int x)
    {
        Console.WriteLine($"Another Main but with a different parameter x={x}");
    }
}

There are even the same errors when you try to overwrite local variable and local function.

So as you can see it is a matter of scopes and you cannot overwrite local function or variable.

BTW, in a first example you could make recursive call by using this.Main();:

void Main()
{
    void Main()
    {
        Console.WriteLine("Hello!");
    }
    this.Main(); // call instance method
}

Footnote: Local functions are not represented as delegates as some commentators suggest and it makes local functions much leaner in both memory and CPU.

Dissecting the local functions in C# 7, The Local functions is a new feature in C# 7 that allows defining a function inside another function. When to use a local function? But this is not always the case. internal struct c__DisplayClass0_0 { public int arg; } public void Local functions can “hide” a function with the same name declared in the  Depending on their use, local functions can avoid heap allocations that are always necessary for lambda expressions. If a local function is never converted to a delegate, and none of the variables captured by the local function is captured by other lambdas or local functions that are converted to delegates, the compiler can avoid heap allocations.

To expand a bit on v-andrew's answer, it is indeed analogous to having two variables with the same name. Consider that the following is allowed:

void Main()
{
    {
        void Main()
        {
            Console.WriteLine("Hello!");
        }
        Main();
    }
    {
        void Main()
        {
            Console.WriteLine("GoodBye!");
        }
        Main();
    }
}

Here we've two scopes and so we can have two local functions of the same name in the same method.

Also to combine v-andrew's answer and your question, note that you can (and always could) have a variable called Main inside Main() but you can't have both a variable and a local function of the same name in the same scope either.

On the other hand, you can't overload locals like you can members by having different parameters.

Really, it's all closer to the existing rules for locals than the existing rules for methods. Indeed, it's the same rules. Consider you can't do:

void Main()
{
    {
        void Main()
        {
            int Main = 3;
            Console.WriteLine(Main);
        }
        Main();
    }
}

I thought, each inner Main() would have a local scope and is hidden outside.

It is, but the scope includes the name of the local function. C.f. that you can't redefine a variable name from a for, foreach or using inside the its scope.

Meanwhile, I think it is a compiler bug.

It's a compiler feature.

That means, removing the L from MainL should not harm because the compiler already renames it in a unique way, it should result in IL code like.

That means it's possible to introduce a bug into the compiler where the code you have in your question would work. That would be in violation to the C# rules for names of locals.

it is confusing in C#, but logical in C++

It blocks something that has been known as a source of mistakes for some time. Likewise in C# you are not allowed to use integer values with if() and you have to explicitly fall-through in switch statements. All of these are changes that C# made to how it compares to C++ in the very beginning and all of them remove some convenience, but all of them are things that people really had found caused bugs and often prohibited in coding conventions.

Thoughts on C# 7 Local Functions, Frankly when I first saw that C# 7 will come with new local functions I thought that We can try to hide our helper inside the scope of Foo method by converting it to there will be no overhead of calling it and it can have all the  The following line creates a named ValueTuple: var tuple = (a:1, b:2, c:3, d:4, e:5, f:6); Value types can not be passed around efficiently. Does C#7 offer a way to create named tuples of the

C# Local Functions, C# Local Functions are an interesting feature released with C# 7.0. C# local def inner_increment(num1): # hidden from outer code. 3. I am here to start a series related C# 7.0 features. Today, we will be going through one of the features called Local Functions and will demonstrate the uses in an easy way. Local Function. Local function is a kind of inner function, sub-function or function within a function that can be declared and defined within parent method.

As c# is a statically compiled language, i'd think all the functions are compiled before their encompassing scope is executed and therefor the innermost Main cannot be declared because a Main already exists from its' closure point of view (one level up).

Note that this is not based on factual evidence but just my initial thoughts on the matter.

After some research... I was wrong, mostly. My gut feeling does seem to imply the same behaviour, But not for the reasons I initially thought.

@PetSerAl already explained in comment form better than I could have copied it from manuals so I defer to that answer.

CS8025 C# Feature 'local functions' is not available , CS8025 C# Feature 'local functions' is not available in C# 4. Please use language version 7 or greater. This code isn't finished yet, i'm putting together all the player control scripts i've written Add comment · Hide 4 · Share. C# 7.0 adds a new feature called local functions, which allows local methods to be defined and called within a method. This wasn’t possible in the previous versions. The following code snippet defines a method named "Add" inside the "Main" method and calls within the "Main" method.

C#7 - Local Functions Are Bringing Cleaner Code , CS8025 C# Feature 'local functions' is not available in C# 4. Please use language version 7 or greater. Just because a type is a nested type of a class doesn't necessarily mean that the type types (the outer is again just syntactical sugar and actually creates a hidden class that will contain that method. Why Local Functions in C#. There are several advantages to local functions. Encapsulation. You use local functions to protect them from anything happening outside of the function, meaning that they are hidden from the global scope. Keeping it DRY. When writing functions you may possibly want to use the same block of code in numerous places.

Contextuality From Quantum Physics To Psychology, It is a function nested within another function and not visible outside of its Important feature is that it can "see" all local variables of its enclosing that is not polluting interface since it is hidden inside enclosing function. Local functions. Local functions can be defined in the body of any method, constructor or property’s getter, and setter. Since it will be transformed by compiler to regular private static method: there will be no overhead of calling it and it can have all the properties which regular method declaration can have: it can be asynchronous,

Discussion: Local Functions · Issue #2930 · dotnet/roslyn · GitHub, where the joint probability function p(J = t, W = i, R = j) = p(J = t|W = i, using the conditional probability tables in Figure 1: p(W = t|J = t) = (.9 × .1 × .7) + (.8 × .1 Consider a causal situation where a variable A causes B and C (represented by that people's causal judgments do not always obey the local Markov property. Local functions can. Local functions look better. This is not mentioned in the above quote and might be just my personal bias, but I think that normal function syntax looks better than assigning a lambda to a delegate variable. Local functions are also more succinct. Compare: int add(int x, int y) => x + y; Func<int, int, int> add = (x, y) => x + y;

Comments
  • 7.6.2.1 Invariant meaning in blocks: For each occurrence of a given identifier as a full simple-name (without a type argument list) in an expression or declarator, within the local variable declaration space (§3.3) immediately enclosing that occurrence, every other occurrence of the same identifier as a full simple-name in an expression or declarator must refer to the same entity.
  • Since behavior of prohibiting same names in nested scopes was in C# from the beginning it would be surprising to have it changed with nested functions... Essentially question is "why C# 7 uses naming rules for nested functions consistent with previous versions of the language rather than inventing new once" :)
  • You get the error for the same reason you always see that error. If you used int identifiers or string identifiers instead of method identifiers, you get the same behavior. There's nothing new here.
  • @Kyle The difference between a method group and a variable isn't actually relevant to this question. The example here is using method groups instead of variables and it in no way changes what's going on. This is simply how locally scoped identifiers work, whether those identifiers are methods, integer variables, or whatever else.
  • To add to what @Servy said, for the record, you can do this "infinite chain" of nesting over top of identifier names (with variables at least) in C++ (try it), but you can't in C#. So that may be confusing some.
  • If that were the case, the second Main() wouldn't be allowed either
  • Outer Main is a class method, where local Main is local so it it can overwrite global Main for the current scope. This is a reason, why local Main is executed in the first example
  • @v-andrew From my limited understanding of C#, the name of a method is something entirely different from the name of a variable. Specifically the name of a method (Main in this case) is a method group. So it's not that it's overwritten, per se, it's that the compiler resolves the name Main to the local variable before it tries to resolve it as a method group.
  • @Kyle We talk about name conflict resolution by the compiler. Look at example with variables instead of functions and you will see analogy
  • In JavaScript I would agree, because there we have 1st class functions. In c# it is a bit different, functions are not exactly treated as object variables.
  • Well - If that was the case I would have expected a warning or error in the 1st and 2nd example, because we are reusing the name 'main'.
  • @Matt not in the same namespace (in the general sense of namespace).
  • In your example (let's name it example E3) it is a different scope created by the extra curly braces. That is not the same as in E1 and E2.