为什么Linq表达式IL会导致省略Select投影,而相应的方法表达式会保留Select投影?
我认为这两段代码都是一样的。
var a = from c in companies
where c.Length >10
select c;
//
var b = companies.Where(c => c.Length > 10).Select(c => c);
//IL - LINQ
IEnumerable<string> a = this.companies.
Where<string>(CS$<>9__CachedAnonymousMethodDelegate1);
//IL
IEnumerable<string> b = this.companies.Where<string>
(CS$<>9__CachedAnonymousMethodDelegate4).Select<string, string>
(CS$<>9__CachedAnonymousMethodDelegate5);
那为什么IL的区别?
已编辑: 那么为什么
var a = from c in companies
select c;
即使在IL内部也会导致SELECT投影。它也可以省略吗?
答案 0 :(得分:5)
C#编译器很聪明,可以从Linq中删除无用的语句。选择c没用,所以编译器将其删除。当你编写Select(c =&gt; c)时,编译器不能说该指令是无用的,因为它是一个函数调用,所以它不会删除它。 如果你自己删除IL就变成了一样。
编辑: Linq是一种“描述性”语言:你说出你想要的东西,编译器就可以很好地转换它。您对该转换没有任何控制权。编译器尝试优化函数调用而不使用Select因为你没有做投影所以它没用。 当你编写Select(c =&gt; c)时,你会明确地调用一个函数,所以编译器不会删除它。
var a = from c in companies select c;
var a = c.Select(elt=>elt);
选择在此示例中很有用。如果你删除它有一个类型的c;否则a是一个IEnumerable
答案 1 :(得分:5)
@mexianto当然是正确的,这是一个编译器优化。
请注意,这在“退化查询表达式”下的语言规范中明确调出。另请注意,编译器非常智能,不执行优化时,会返回原始源对象(用户可能希望使用简并查询使客户端难以改变源对象,假设它是可变的。)
7.16.2.3简并查询表达式
表单
的查询表达式
from x in e select x
被翻译成
( e ) . Select ( x => x )
[...]退化查询表达式就是这样 平凡地选择源的元素。后期的 翻译删除其他翻译引入的退化查询 用它们的来源替换它们的步骤。但重要的是 确保查询表达式的结果永远不是源 对象本身,因为它会揭示出的类型和身份 源到查询的客户端。因此这一步保护 通过显式直接在源代码中编写的简并查询 调用源上的选择。然后由实施者来决定 选择和其他查询运算符以确保从不这些方法 返回源对象本身。
答案 2 :(得分:3)
在第二个示例中,对Select的调用不是多余的。如果省略Select调用,查询将只返回原始集合,而Select返回IEnumerable。
在你的第一个例子中,Where已经返回一个IEnumerable而select子句没有做任何工作,所以省略了它。
答案 3 :(得分:1)
因为在查询版本中没有实际选择将'c'投射到其他内容中,所以它只是按原样传递'c'。这导致只调用'Where'。
在第二个变体中,您明确地调用“选择”,从而进行投影。是的,您只返回相同的对象,但编译器不会看到这个。