在多个链式LINQ查询中使用一个变量

时间:2014-04-17 15:31:09

标签: c# vb.net linq lambda

在多个链式调用中使用单个lambda表达式变量是否公平?例如:

MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);

i中使用的Where()仍然独立于i中使用的OrderBy(),或者它们之间可能存在一些隐藏的副作用,所以我必须这样做每个使用不同的变量?另外,您的答案也适用于VB.NET吗?

我问这个是因为我在稍微不同的上下文中读过我不应该直接在LINQ查询中使用foreach变量,而是在循环中创建变量的本地副本。上面的代码中是否也隐藏了一些类似的效果?

4 个答案:

答案 0 :(得分:6)

他们是完全独立的。实际上,每次声明一个lambda时,你都声明了范围变量,这些变量是这个labda表达式的局部变量。因此i中的Where(i => i.ID > 20)i中的OrderBy(i => i.Name)完全不同。在第一种情况下,i引用MyList的随机元素,然后i引用来自Where子句的序列的随机元素, MyList的所有元素的序列ID>20

答案 1 :(得分:4)

详细说明一下,这很好:

MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);

这两个名为i的变量是完全独立的,只存在于各自的lambda表达式的上下文中。但是,这不是:

int i = 0;
MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);

现在,你的lambda中的i与父范围中定义的i冲突。

foreach的问题比原始问题稍微微妙一些。如果你有这个:

foreach (var foo in fooList) 
{
    var filteredList = MyList.Where(i => i.ID > foo.Id).OrderBy(i => i.Name);
}

这里的问题是因为LINQ使用延迟执行并引用循环变量foo所以它将创建一个包含foo的闭包。问题是,它没有复制 foo它实际上有一个对变量的引用。所以当你最终通过迭代执行你的lambda时,或者:

var bar = filteredList.ToList();

foo lambda中Where的值将是foo 现在的值,而不是声明lambda时的值。因此foo始终是fooList中的最后一项。复制变量修复了这个问题,因为现在它将关闭那个不同的变量(只存在于循环的那一次迭代)而不是循环变量。

foreach (var foo in fooList) 
{
    var copy = foo;
    var filteredList = MyList.Where(i => i.ID > copy.Id).OrderBy(i => i.Name);
}

答案 2 :(得分:3)

Lambda表达式基本上是编写匿名方法的一种较短方式。因此,lambda示例中的i与匿名方法中的参数相同。换句话说,它们彼此独立,正如不同方法的参数彼此独立一样。

为了便于阅读,将i更改为person或类似内容可能是值得的。例如。 MyList.Where(person => person.ID > 20).OrderBy(person => person.Name);

关于foreach:在Eric Lippert的博客中了解此背景下的闭包:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

摘录:

  

因为()=> v表示"返回变量v"的当前值,而不是"返回值v在创建委托时返回"。闭包关闭变量,而不是值。当方法运行时,显然分配给v的最后一个值是120,所以它仍然具有该值。

答案 3 :(得分:2)

请参阅:Variable Scope in Lambda Expression - MSDN

  

lambda表达式中引入的变量在。中不可见   外部方法。

因此,对于您的Where子句,您声明i仅在Where子句中可见,因此独立于OrderBy子句中声明的子句。