Lambda捕获参数引发歧义

本文关键字:歧义 参数 Lambda | 更新日期: 2023-09-27 17:50:35

我刚刚遇到了一个非常奇怪的C#行为,如果有人能向我解释一下,我会很高兴。

比方说,我有以下课程:

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
    }
}

正如我所理解的,在评论行中,我的视野中有以下对象:len变量和call。在lambda内部,我有局部参数lenProgram.len变量。

但是,在声明了这样的lambda之后,我就不能再在Main方法的作用域中使用len变量了。我要么把它称为Program.len,要么把lambda重写为anyOtherNameBesidesLen => 1

为什么会这样?这是正确的语言行为,还是我在语言中遇到了错误?如果这是正确的行为,那么语言架构如何证明它的合理性?为什么lambda捕获变量可以干扰lambda之外的代码?

编辑:亚历山德罗·德安德里亚(Alessandro D'Andria(有一些很好的例子(在他的评论中有1号和2号(。

第2版:这个代码(相当于我在开头写的代码(是非法的:

class Program
{
    static int len = 0;
    static void Main(string[] args)
    {
        {
            int len = 1;
        }
        int x = len;
    }
}

然而,尽管该代码具有完全相同的范围结构,但它是完全合法的:

class Other
{
    static int len = 0;
    class Nested
    {
        static void foo()
        {
            int len = 1;
        }
        static int x = len;
    }
}

Lambda捕获参数引发歧义

据我所见,在这种情况下发出编译时错误是正确的,因为当在同一方法内的包含范围中使用相同的符号len(用于其他内容(时,不允许在子范围(即匿名函数(lambda(的参数(中使用len

然而,错误文本令人困惑。

如果您更改为:

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
}

错误文本更好。


其他一些例子:

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
}

上面的例子表明,隐藏具有相同名称的局部变量(或方法参数(的字段是可以的,只要该字段总是通过限定访问(在.成员访问运算符之后(。

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'
}

这表明,当您尝试在父作用域中使用字段len时,不可能在子作用域中隐藏len。同样,错误文本可能会受到批评。

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);
    }
}

你看到了这个比喻。


当然,这些问题以前在SO上已经提到过很多次了,例如为什么不能在嵌套的本地作用域中声明重复的变量名?

错误消息是错误的,它应该指向包含lambda的行。

正如您所说的"在lambda内部,我有局部参数lenProgram.len变量",问题是lambda参数的名称与变量len冲突。所以问题只出在lambda表达式中。问题不在于"不能再在Main方法的作用域中使用len变量">。这是两者之间的冲突,根本不会再编译任何代码。