【发布时间】:2012-02-02 16:09:10
【问题描述】:
如何使用扩展方法语法编写此 LINQ 查询?
var query = from a in sequenceA
from b in sequenceB
select ...;
【问题讨论】:
如何使用扩展方法语法编写此 LINQ 查询?
var query = from a in sequenceA
from b in sequenceB
select ...;
【问题讨论】:
C# 规范的第 7.16 节回答了此表单的所有问题。
本段回答了您的具体问题:
一个带有第二个 from 子句的查询表达式,后跟一个 select 子句
from x1 in e1
from x2 in e2
select v
被翻译成
( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )
所以你的查询:
var query = from a in sequenceA
from b in sequenceB
select ...;
是一样的
var query = ( sequenceA ) . SelectMany( a => sequenceB , ( a , b ) => ... )
(请注意,这当然假定“...”是一个表达式,而不是说,一个表达式后跟一个查询继续。)
hdv 的回答指出
var query = ( sequenceA ) . SelectMany(
a => ( sequenceB ) . Select( b => ... ) );
也将是一个逻辑上有效的翻译,尽管它不是我们实际执行的翻译。在 LINQ 实现的早期,这是我们选择的翻译。然而,当你堆积更多的from 子句时,它会使 lambda 嵌套越来越深,这会给编译器带来类型推断中的巨大问题。这种翻译选择会破坏编译器的性能,因此我们引入了透明标识符机制,为我们提供了一种更便宜的方式来表示深度嵌套范围的接缝。
如果您对这些主题感兴趣:
要详细了解为什么深度嵌套的 lambda 会给编译器带来一个难以解决的难题,请参阅:
有关透明标识符的更多信息,请参阅 Wes Dyer 的这篇文章,他在 C# 3.0 中实现了它们:
http://blogs.msdn.com/b/wesdyer/archive/2006/12/22/transparent-identifiers.aspx
还有我关于它们的系列文章:
http://ericlippert.com/2014/07/31/transparent-identifiers-part-one/
【讨论】:
var query = sequenceA.SelectMany(a => sequenceB.Select(b => ...));
编辑:正如 Eric Lippert 在 cmets 中指出的那样,这给出了相同的结果,但故意不是内部翻译的方式。请参阅他的答案以了解另一种致电SelectMany 的方式,该方式确实与原始方式相对应。另外,为了清楚起见,添加了省略的b =>。
【讨论】:
var query = sequenceA.SelectMany(a => sequenceB).Select(...); 这样的非嵌套变体也可以吗?
Select 的参数中引用a。
a。但是在您的两个翻译中,您都丢失了范围变量b。 b 在你的翻译中去了哪里?如果... 指的是b 怎么办?你需要另一个 lambda 在某个地方!
SelectMany 重载编写它,但据我了解,这与我回答的含义相同。如果有错误请纠正我,我将不胜感激。
另一种写法是:
var query = a.Join(b, i => new { }, j => new { }, (i, j) => new { i = i, j = j });
【讨论】:
from-statements 和SelectMany 都将嵌套的枚举展开。 IE。它们将枚举的枚举转换为一个平面枚举,其中包含源的嵌套枚举的所有元素。他们不加入两个不相关的枚举。
string[][] nested = new string[][] { new string[] { "a", "b", "c" }, new string[] { "AA", "BB", "CC", "DD" }, new string[] { "1", "2", "3" }, };。预期输出:"a", "b", "c", "AA", "BB", "CC", "DD", "1", "2", "3"