Lambda capture parameter provoking ambiguity
I just bumped into a really strange C# behavior, and I'd be glad if someone could explain it to me.
Say, I have the following class:
class Program
{
static int len = 1;
static void Main(string[] args)
{
Func<double, double> call = len => 1;
len = 1; // error: 'len' conflicts with the declaration 'csutils.Program.len'
Program.len = 1; // ok
}
}
As I get it, in the line of commentary I have the following objects in my field of view: len
variable and call
. Inside of lambda, I have local parameter len
and Program.len
variable.
However, after declaring such lambda, I cannot use len
variable in the scope of Main
method anymore. I have to either refer to it as to Program.len
either rewrite lambda to be anyOtherNameBesidesLen => 1
.
Why is it happening? Is this correct behavior of language, or I've encountered a bug in the language? If this is correct behavior, how is it justified by the language architecture? Why is it okay for lambda capture variable to mess with code outside lambda?
Edit: Alessandro D'Andria has got quite nice examples (number 1 and 2 in his comment).
edit2: This code (equal to the one I wrote at the beginning) is illegal:
class Program
{
static int len = 0;
static void Main(string[] args)
{
{
int len = 1;
}
int x = len;
}
}
This code however, despite having exactly the same scope structure, is perfectly legal:
class Other
{
static int len = 0;
class Nested
{
static void foo()
{
int len = 1;
}
static int x = len;
}
}
As far as I can see, it is correct to emit a compile-time error in this case because it is not permissible to use len
in a child scope (namely the parameter of the anonymous function (lambda)) when the same symbol len
without qualification is used (for something else) in a containing scope within the same method.
However, the error text is confusing.
If you change to:
static int len = 1;
static void Main(string[] args)
{
len = 1;
Func<double, double> call = len => 1; // error CS0136: A local variable named 'len' cannot be declared in this scope because it would give a different meaning to 'len', which is already used in a 'parent or current' scope to denote something else
}
the error text is better.
Some other examples:
static int len = 1;
static void Main()
{
var len = 3.14; // OK, can hide field
Console.WriteLine(len); // OK, 'len' refers to local variable
Console.WriteLine(Program.len); // OK, hidden field can still be accessed, with proper qualification
}
The above example shows that it is fine to hide a field with an identically named local variable (or method parameter) as long as the field is always accessed with qualification (after a .
member access operator).
static int len = 1;
static void Main()
{
if (DateTime.Today.DayOfWeek == DayOfWeek.Saturday)
{
var len = 3.14;
Console.WriteLine(len);
}
Console.WriteLine(len); // error CS0135: 'len' conflicts with the declaration 'csutils.Program.len'
}
This shows that it is not possible to hide len
in a child scope when you try to use the field len
in the parent scope. Again the error text can be criticized.
static int len = 1;
static void Main()
{
Console.WriteLine(len);
if (DateTime.Today.DayOfWeek == DayOfWeek.Saturday)
{
var len = 3.14; // error CS0136: A local variable named 'len' cannot be declared in this scope because it would give a different meaning to 'len', which is already used in a 'parent or current' scope to denote something else
Console.WriteLine(len);
}
}
You see the analogy.
Of course these issues have been mentioned many times before here on SO, an example being Why can't a duplicate variable name be declared in a nested local scope?
The error message is erroneous and it should point to the line containing the lambda.
As you say "Inside of lambda, I have local parameter len
and Program.len
variable", the problem is that the name of the lambda parameter conflicts with the variable len
. So the problem is in the lambda expression only. The problem is not that you "cannot use len
variable in the scope of Main
method anymore". It is a conflict between the two and no code at all will compile anymore.
上一篇: 是否应该恢复Thread.CurrentContext?
下一篇: Lambda捕获参数引起歧义