Why does this code work? I'm using C# 8 with Visual Studio 2019.
You've answered your own question! It's because you're using C# 8.
The rule from C# 1 through 7 was: a simple name cannot be used to mean two different things in the same local scope. (The actual rule was slightly more complex than that but describing how is tedious; see the C# specification for details.)
The intention of this rule was to prevent the sort of situation that you're talking about in your example, where it becomes very easy to be confused about the meaning of the local. In particular, this rule was designed to prevent confusions like:
class C
{
int x;
void M()
{
x = 123;
if (whatever)
{
int x = 356;
...
And now we have a situation where inside the body of M
, x
means both this.x
and the local x
.
Though well-intentioned, there were a number of problems with this rule:
- It was not implemented to spec. There were situations where a simple name could be used as, say, both a type and a property, but these were not always flagged as errors because the error detection logic was flawed. (See below)
- The error messages were confusingly worded, and inconsistently reported. There were multiple different error messages for this situation. They inconsistently identified the offender; that is, sometimes the inner usage would be called out, sometimes the outer, and sometimes it was just confusing.
I made an effort in the Roslyn rewrite to sort this out; I added some new error messages, and made the old ones consistent regarding where the error was reported. However, this effort was too little, too late.
The C# team decided for C# 8 that the whole rule was causing more confusion than it was preventing, and the rule was retired from the language. (Thanks Jonathon Chase for determining when the retirement happened.)
If you are interested to learn the history of this problem and how I attempted to fix it, see these articles I wrote about it:
https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/
https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/
https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/
https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/
https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/
At the end of part three I noted that there was also an interaction between this feature and the "Color Color" feature -- that is, the feature that allows:
class C
{
Color Color { get; set; }
void M()
{
Color = Color.Red;
}
}
Here we have used the simple name Color
to refer to both this.Color
and the enumerated type Color
; according to a strict reading of the specification this should be an error, but in this case the spec was wrong and the intention was to allow it, as this code is unambiguous and it would be vexing to make the developer change it.
I never did write that article describing all the weird interactions between these two rules, and it would be a bit pointless to do so now!