为啥 Lambda 变量范围存在于 LINQ 查询之外?

Posted

技术标签:

【中文标题】为啥 Lambda 变量范围存在于 LINQ 查询之外?【英文标题】:Why Lambda variable scope exists outside LINQ Query?为什么 Lambda 变量范围存在于 LINQ 查询之外? 【发布时间】:2012-05-23 07:14:09 【问题描述】:

我看了这个问题(What is the scope of a lambda variable in C#?)

但它是关于 LINQ Query 中的 Lambda 变量范围。

现在我的问题

假设我有一个非常简单的 LINQ 查询。

var Foo = FoobBar.Select(x => x);
var x = somefunction();

编译器说:A local variable 'x' cannot be declared in this scope because it would give a different meaning to 'x', which is already used in a 'child' scope to denote something else.

为什么会这样? LINQ 查询结束时,Lambda 变量不应该不复存在吗?

编辑:阅读答案后,我得出结论,它的外部 x(从函数返回)其范围扩展到 LINQ 查询内部。

【问题讨论】:

在阅读了所有答案后,我得出的结论是反过来。外部变量的范围存在于 LINQ 查询中。 反之亦然 ;-) 如果仅仅因为冲突变量 (var x = somefunction();) 位于第二行而允许代码编译,则该语言不会对重构友好,然后当您将第二行移到第一行时,令您非常懊恼的是,它将不再编译。 C# 有一个先发制人的机制,它不允许 var x = someFunction() 编译,即使它在第二行,因为您可能随时重构代码并稍后将其放在第一行。我们生活在一个可重构代码成为常态的时代 【参考方案1】:

这与 LINQ 无关,而与子作用域有关。

例如:

foreach (var bar in FooBar.Bars)
        
            var x = FooBar.GetFoo();
        
var x = new Foo();

从编译器产生完全相同的错误消息。

要解决这个问题,您只需将变量放在不同的(非嵌套)范围内。例如:

foreach (var bar in FooBar.Bars)
        
            var x = FooBar.GetBar();
        

    var x = new Foo();

【讨论】:

【参考方案2】:

让我们仔细看看,

var Foo = FoobBar.Select(x => x);

true x 的范围以表达式结尾

var x = somefunction()

现在这很有趣,这也适用于包含 lamda 表达式的整个方法,因此编译器无法区分,因为后者的范围与前者重叠。而且信息也非常丰富赋予“x”不同的含义,它已经在“子”范围内使用 (根据您的情况选择)

Similar scope question

也许你可以在另一个周围包含大括号,以便定义它的范围


var x = somefunction()

【讨论】:

在最后一个sn-p中,这意味着x的范围被限制在那些大括号内?【参考方案3】:

想想如果 C# 允许这两个变量存在于同一范围级别,这是不可能的:

static void test() 

    Action<int> x = (z) => 
        Console.WriteLine(z);               
    ;


    int[] i =  5,2,0,1,3,1,4 ;

    var kyrie = i.Select (x => x);


你会如何对 C# 说你想将名为 x 的 Action 委托分配给 kyrie 变量;反之亦然,你会如何对 C# 说你想使用整数投影本身? C# 将如何解决这个问题?

而且C#的作用域解析是非常一致的,无论你是在其他变量之前还是在其他变量之后声明,它们都是一样的。例如http://www.anicehumble.com/2012/05/java-said-c-said-scope-consistency.html

为了消除这些情况的歧义,C# 不允许您编译具有相同级别的变量名的程序

这一切都是为了以明确的方式向编译器表达您的意图。而 C# 在这方面做得很好

【讨论】:

+1 表示“这一切都是为了以明确的方式向编译器表达你的意图”。完全正确。【参考方案4】:

不清楚(编译器)第二个“x”指的是哪个“x”(在=&gt; 之后)。

如果你写这个会怎样:

var Foo = FoobBar.Select(y => x);
var x = somefunction();

那么 lambda 中的 x 将与“somefunction”结果发生冲突。

【讨论】:

【参考方案5】:

您不能在同一范围内声明两个具有相同名称的变量。

“相同范围”意味着它们都在当前范围内

void method()

    int a;
    int a; //wrong!

或者一个在当前范围内,另一个在子范围内。

void method()

    int a;
    for(;;)
    
        int a; //wrong again!
    

这是设计使然,适用于任何变量,从 ints 到 lambda 引用。

【讨论】:

以上是关于为啥 Lambda 变量范围存在于 LINQ 查询之外?的主要内容,如果未能解决你的问题,请参考以下文章

Linq之关键字基本查询

LINQ查询表达式---------let子句

点标记(lambda表达式+linq查询标记符)与linq语句(查询表达式)

linq 和lambda查询

linq/lambda 查询中的双内连接?

如何在 LINQ 和 Lambda 表达式 LINQ C# 上正确执行 SQL 查询