I've got a weak imagination when it comes to names, so I often find myself re-using identifiers in my code. This caused me to run into this specific problem.
Here's some example code:
public delegate void TestDelegate(int test);
public class Test
{
private int test;
private void method(int aaa)
{
TestDelegate del = test => aaa++;
test++;
}
public static void Main()
{
}
}
Here are the compilation errors (output by ideone):
prog.cs(11,3): error CS0135: `test' conflicts with a declaration in a child block
prog.cs(9,22): (Location of the symbol related to previous error)
Compilation failed: 1 error(s), 0 warnings
Line 11 contains test++
, line 9 contains the lambda.
Incidentally, Visual Studio 2013 gives a different error:
'test' conflicts with the declaration 'Namespace.Test.test'
The error occurs at the increment on line 11 only.
The code compiles successfully if I comment out either line 9 (the lambda) or line 11 (the increment).
This issue is a surprise to me - I was sure that lambda parameter names can conflict only with local method variable names (which is sort of confirmed by the code compiling when I comment out the increment). Also, how can the lambda parameter possibly affect the increment, which is right outside the lambda's scope?
I can't get my head around this... What exactly did I do wrong? And what do the cryptic error messages mean in this case?
EDIT after all the great answers:
So I think I finally understood the rule that I broke. It is not well-worded in the C# spec (7.6.2.1, see Jon Skeet's answer for the quote). What it was supposed to mean is something like:
You can not use the same identifier to refer to different things (entities) in the same "local variable declaration space" if one of the offending uses is (directly) located in a scope which can be "seen" from the scope where the other is (directly) located.
Not the standard's standard phrasing, but I hope you understood what I mean. This rule was supposed to allow this:
{
int a;
}
{
int a;
}
because neither of the scopes of the two variables a
can be "seen" from the other's scope;
and disallow this:
{
int a;
}
int a;
because the second variable declaration is "seen" from the first variable's scope
and disallow this:
class Test
{
int test;
void method()
{
{
int test;
}
test++;
}
}
because the increment of the field can be "seen" from the block's scope (it not being a declaration doesn't matter).
It seems that C#6 changed this rule, specifically making the last example (and my original code) legit, though I don't really understand how exactly.
Please correct me if I made some mistakes in these examples.
The unfortunately downvoted answer of Sriram Sakthivel is correct. C# has a rule, which I have written about a number of times, which requires that every usage of the same simple name throughout a block to have the same meaning.
I agree that the error message is extremely confusing. I did extensive work in Roslyn to ensure that this error message was less confusing in Roslyn, work which may have been for naught.
You can read my articles about this rule, and the work I did to improve the error message, here:
http://ericlippert.com/tag/simple-names/
(Start at the bottom; these are in reverse-chronological order.)
Jon and others correctly note that in prerelease versions of C# 6, you don't get the error for your code. I believe that after I left the team, more work was done on this error condition, and likely it has been relaxed to be more lenient.
The principle of "one name must mean only one thing" is a good one, but it is tricky to implement, tricky to explain, and the source of a lot of questions on this site, so probably the design team decided to go with an easier-to-explain-and-implement rule. What exactly that rule is, I don't know; I have not yet had time to browse the Roslyn source code and see how that part of the code has evolved over time.
UPDATE: My spies in the Roslyn team inform me that it was commit 23891e, which will shorten the search considerably. :-)
UPDATE: The rule is gone for good; see Jon's answer for details.
Eric Lippert has blogged about this Simple names are not so simple.
Simple names(without a fully qualified name) can always mean only one thing in a block of code. If you violate it there will be a CS0135 compiler error.
In your method method
, test
is a simple name, which means two things. It is not allowed in c#.
If you make the test field you use a qualified name instead of simple name, compiler error will go away.
private void method(int aaa)
{
TestDelegate del = test => aaa++;
this.test++;
}
Or, if you make the test field access in a different block, compiler will be happy to compile.
private void method(int aaa)
{
TestDelegate del = (int test) => aaa++;
{
test++;
}
}
Now you don't have two different meaning for the same simple name test
. because second test lives in a different block.
As of now(april 2015) this answer is valid. Starting from C#6.0 things has changed. This rule has gone away. Refer Jon's answer for details.
I raised Roslyn issue 2110 for this - the C# 6 spec is changing to allow this. Mads Torgersen has indicated that the change is by design:
The rule was well intended, in that it was supposed to minimize the risk of "moving code around" in a way that would silently change its meaning. However, everyone we talked to only seemed to know about the rule from when it was causing them unnecessary grief - no-one had ever been saved by its prohibitions.
Furthermore, the extra tracking necessary in the compiler to enforce the rule interactively was causing significant complications to the code, as well as non-trivial performance overhead. Now, if it were a good rule we would have kept it anyway, but it isn't! So it's gone.
It's simple to demonstrate the inconsistency between compiler versions without any delegates involved:
class Test
{
static int test;
static void Main()
{
{
int test = 10;
}
test++;
}
}
The related section of the C# 5 spec is 7.6.2.1, which gives this rule:
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. This rule ensures that the meaning of a name is always the same within a given block, switch block, for-, foreach- or using-statement, or anonymous function.
Personally, I think this is a less-than-ideal bit of spec. It isn't clear whether "within a given block" is meant to include nested blocks. For example, this is fine:
static void Main()
{
{
int x = 10;
}
{
int x = 20;
}
}
... despite the fact that x
is used to refer to different variables within the "top-level" block of the Main
method, if you include nesting. So if you consider that block, it violates the claim that "this rule ensures that the meaning of a name is always the same within a given block [...]" However, I believe that block isn't checked for this, because it isn't the "immediately enclosing" variable declaration space of any use of x
.
So in my view, the error is consistent with the first quoted part of the spec, but isn't consistent with the final sentence, which is a note in the ECMA spec.
I will make a note of the poor wording of the spec, and try to fix it for the next version of the ECMA spec.