为什么这个LINQ查询编译?

mcr*_*ley 5 .net c# linq linq-query-syntax

在阅读Jon Skeet的" Odd query expressions "后,我尝试了下面的代码.我期望最后的LINQ查询转换为int query = proxy.Where(x => x).Select(x => x);不编译因为Where返回一个int.代码编译并将"Where(x => x)"打印到屏幕并将查询设置为2.从不调用Select,但需要在那里编译代码.怎么了?

using System;
using System.Linq.Expressions;

public class LinqProxy
{
    public Func<Expression<Func<string,string>>,int> Select { get; set; }
    public Func<Expression<Func<string,string>>,int> Where { get; set; }
}

class Test
{
    static void Main()
    {
        LinqProxy proxy = new LinqProxy();

        proxy.Select = exp => 
        { 
            Console.WriteLine("Select({0})", exp);
            return 1;
        };
        proxy.Where = exp => 
        { 
            Console.WriteLine("Where({0})", exp);
            return 2;
        };

        int query = from x in proxy
                    where x
                    select x;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 12

这是因为你的"选择x"实际上是一个无操作 - 编译器不打扰最后Select(x => x)调用.如果您删除了该条款,它where.您当前的查询称为简并查询表达式.有关详细信息,请参阅C#4规范的第7.16.2.3节.特别是:

简并查询表达式是一种简单地选择源元素的表达式.翻译的后期阶段通过用其源替换它们来移除由其他翻译步骤引入的退化查询.但是,确保查询表达式的结果永远不是源对象本身是很重要的,因为这会向查询客户端显示源的类型和标识.因此,此步骤通过在源上显式调用Select来保护直接在源代码中编写的退化查询.然后由Select和其他查询运算符的实现者来确保这些方法永远不会返回源对象本身.

所以,三个翻译(不论数据来源)

// Query                          // Translation
from x in proxy                   proxy.Where(x => x)
where x
select x


from x in proxy                   proxy.Select(x => x)
select x               


from x in proxy                   proxy.Where(x => x)
where x                                .Select(x => x * 2)
select x * 2
Run Code Online (Sandbox Code Playgroud)


Tim*_*mwi 7

它编译是因为LINQ查询语法是词法替换.编译器转向

int query = from x in proxy
            where x
            select x;
Run Code Online (Sandbox Code Playgroud)

int query = proxy.Where(x => x);     // note it optimises the select away
Run Code Online (Sandbox Code Playgroud)

只有然后它检查是否方法WhereSelect对类型实际存在proxy.因此,在您给出的具体示例中,Select实际上并不需要为此进行编译.

如果你有这样的事情:

    int query = from x in proxy
                select x.ToString();
Run Code Online (Sandbox Code Playgroud)

那么它会变成:

int query = proxy.Select(x => x.ToString());
Run Code Online (Sandbox Code Playgroud)

并且Select将调用该方法.