我通常喜欢使用扩展方法,因为我认为它们更易于阅读,但在看到Erno对这个问题的回答后,我想知道仅使用扩展方法的最小查询会是什么样子?
更一般地说,有没有一种形式可以创建查询而另一种形式却不行,还是两种方法都是等效的?
来自ILSpy:
这个
var minimum = (from p1 in differenceList
from p2 in differenceList
let distance = Math.Abs(p1.X - p2.X)
where !object.ReferenceEquals(p1, p2)
orderby distance
select new { Point1 = p1, Point2 = p2, Distance = distance }).First();
经过一些清理和注释后
var minimum = differenceList
// The two from
.SelectMany(
p1 => differenceList,
(p1, p2) =>
new {
p1 = p1,
p2 = p2
})
// The let
.Select(q =>
new{
q = q,
distance = Math.Abs(q.p1.X - q.p2.X)
})
// The where
.Where(r => !object.ReferenceEquals(r.q.p1, r.q.p2))
// The orderby
.OrderBy(r => r.distance)
// The final select
.Select(r => new
{
Point1 = r.q.p1,
Point2 = r.q.p2,
Distance = r.distance
})
// The First
.First();
说实话,唯一我不知道如何“手动”完成的就是两个from
。我猜想它应该是一个SelectMany
,但至少得花30分钟才能弄明白。如果你感兴趣,在ILSpy中选择选项->反编译器
并取消勾选“反编译查询表达式”。
在没有查询表达式的情况下,您可以做到的事情,在有查询表达式的情况下也同样可以做到 - 查询表达式只是被翻译成非查询表达式的代码而已。然而,有很多查询无法使用查询表达式编写...例如,任何使用Select
重载函数并同时提供索引的情况:
var foo = bar.Select((value, index) => new { value, index });
当然,还有许多运算符在查询表达式中根本不支持(如First
等)。
最基本的查询将使用SelectMany
作为第二个from
子句,Select
作为let
子句(引入新的透明标识符),Where
作为where
子句,以及Select
作为select
子句。
有些查询只能使用扩展方法语法编写(特别是一些查询语法不支持的扩展方法)。扩展方法语法支持查询语法支持的所有功能,因为查询语法被编译成完全相同的扩展方法。
另一方面,查询语法具有一些在扩展方法语法中更加冗长的特性(例如let
和某些join
)。
join
可以用SelectMany
替换,而let
则可以用一个Select
来代替,该Select
引入了一个匿名类型,其中包括查询中的实际变量和let
子句中引入的变量。
扩展方法语法中的清晰版本如下:
differenceList
.SelectMany(p1=>differencelist,(p1,p2) => new {Point1 = p1,Point2 = p2,
Distance=Math.Abs(q.p1.X - q.p2.X)})
.Where(e=>!object.ReferenceEquals(e.p1,e.p2))
.OrderBy(e=>e.Distance)
.First();