18

Historically, when developing in .Net I could not duplicate the name of variable in nested scope. However, after recently updating Visual Studio 2019 to version 16.4.2 I have noticed that variable names can be duplicated in nested scope.

For example:

var test = "hello";
Console.WriteLine(test);
var things = new []{"one", "two", "three"};
things.Select(test => // <- test is duplicated here, normally this breaks compilation
{
    Console.WriteLine(test);
    return test;
}).ToList();

// output:
// hello
// one
// two
// three

https://dotnetfiddle.net/h85BK4

Why is this suddenly allowed?

Follow up question: If this is a new language "feature", is there a way to configure Visual Studio to continue to break when a variable is duplicated in nested scope?

masterjeef
  • 226
  • 1
  • 5
  • I can confirm these results. I get a compiler error when targeting .Net Core 2.1, but do not get a compiler error when targeting .Net Core 3.1. –  Jan 10 '20 at 20:15
  • This was introduced in .NET Core 3, though I don't recall it being mentioned. – Emanuel Vintilă Jan 10 '20 at 20:15
  • Select, Where, Skip, Sum, etc are all methods that have their own scopes. Any variable you use inside does not have to be initialized or can later be used outside of the Select statement as well. Its an arbitrary variable used as a selector to iterate over a list./ – Jawad Jan 10 '20 at 20:16
  • 2
    i don't think its allowing duplicate variable. its using the same `test` variable since data type is same. try declaring `test` variable as `int` eg. `var test = 12345;` i think you should get compile error. ( I have not tried it) – LP13 Jan 10 '20 at 20:17
  • It just hides a local variable – Pavel Anikhouski Jan 10 '20 at 20:18
  • 1
    @LP13 Changing the type of `test` as suggested has no effect. You can try it in the Fiddle linked in the question. –  Jan 10 '20 at 20:22
  • @LP13 you get a compiler error for var, not for having duplicate `test` variables. The new one hides the old one, thats it. You can confirm this by changing the value of `test` inside the inner scope and you can see that the outer scoped test is unchanged. –  Jan 10 '20 at 20:22
  • @LP13 Tested; the type of the local variable doesn't matter – Emanuel Vintilă Jan 10 '20 at 20:23
  • Interesting, that's actually an expected behavior in JavaScript. Didn't know that it was disallowed in C# (before) – Orkhan Alikhanov Jan 10 '20 at 20:30
  • @Amy I've run it on both .NET Core 2.1 and 3.1, and it works, without errors – Pavel Anikhouski Jan 10 '20 at 20:30
  • @PavelAnikhouski Interesting. I wonder why we got different results? –  Jan 10 '20 at 20:31
  • @PavelAnikhouski It doesn't work for me on .NET Core 2.1, though it does work on .NET Core 3 and .NET Core 3.1 – Emanuel Vintilă Jan 10 '20 at 20:32
  • 1
    Nevermind, the target framework is irrelevant. The new behavior is due to the language version. The code compiles on C# 8, it doesn't compile on C# 7.3 (and below I assume) – Emanuel Vintilă Jan 10 '20 at 20:34
  • 3
    https://github.com/dotnet/csharplang/issues/2777 https://github.com/dotnet/roslyn/issues/38377 – Orkhan Alikhanov Jan 10 '20 at 20:35
  • @Amy it will fail on .NET 4.7.2 _error CS0136: A local or parameter named 'test' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter_, it think the lang version is point here – Pavel Anikhouski Jan 10 '20 at 20:36
  • @OrkhanAlikhanov Nice find! – Emanuel Vintilă Jan 10 '20 at 20:36
  • 6
    There is GitHub [issue](https://github.com/dotnet/roslyn/issues/38377) for that. Milestone is set to 16.5. @OrkhanAlikhanov you've found it quicker:) – Pavel Anikhouski Jan 10 '20 at 20:37
  • the case here still gives a compile error as before. https://stackoverflow.com/q/6156449/73226 – Martin Smith Jan 10 '20 at 20:47
  • @MartinSmith The new "feature" only applies to lambdas and local functions. – Emanuel Vintilă Jan 10 '20 at 20:52

2 Answers2

1

This is a new feature in C# 8.0, local function and lambda parameters can shadow outer names.

Sebastian 506563
  • 6,980
  • 3
  • 31
  • 56
0

my guess is that the select function is being compiled as static since it has no connections to the host method body. it just prints and returns the item in question. this does not require any access to the method and as such, it can for optimization reasons be moved out of the method body and put on its own within the global scope.

and in this case, that test variable is its own variable and has no connections to the above test variable.

as for preventing this, you can't tell vs to throw an error when it occurs, you will just have manually change the variable name.