Linq where关键字vs. Where扩展名和Expression参数

时间:2010-03-03 14:54:28

标签: linq delegates extension-methods

将表达式传递给Linq查询的行为取决于所使用的语法,我想知道为什么会这样。

假设我有这个非常通用的功能

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)

以下实施按预期工作

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return (from c in _ctx.Companies.Where(whereClause) select c);
}

但是下一个实现不会编译 (委托'System.Func'不带1个参数)

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return (from c in _ctx.Companies where whereClause select c);
}

显然我可以使用第一种语法,但我只是想知道为什么编译器不会将where关键字视为Where扩展名?

谢谢, 托马斯

3 个答案:

答案 0 :(得分:3)

涉及where子句的查询表达式的语法是(简化完整的语法)

from identifier in expression where boolean-expression select expression

whereClause不是布尔表达式。要重新说明这一点,你必须说

from c in _ctx.Companies where whereClause.Compile()(c) select c;

请注意,如果whereClauseFunc<Company, bool>,则可以使用

from c in _ctx.Companies where whereClause(c) select c;

请注意

from x in e where f

由编译器机械翻译成

(from x in e).Where(x => f)

我机械地说,因为它执行此转换而不进行任何语义分析来检查方法调用的有效性等。该阶段在所有查询表达式被转换为LINQ方法调用表达式之后出现。

特别是

from c in _ctx.Companies where whereClause select c

被翻译为

_ctx.Companies.Where(c => whereClause).Select(c)

这显然是荒谬的。

原因

from c in _ctx.Companies.Where(whereClause) select c

是合法的,因为IEnumerable<Company>.Where有超载接受Func<Company, bool>并且存在从Expression<Func<Company, bool>>Func<Company, bool>的隐式转换。

答案 1 :(得分:2)

你实际上可以将整个事情缩短为:

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return _ctx.Companies.Where(whereClause);
}

使用LINQ语法时,where子句中的代码将转换为Expression<>,代表代码树。当你接受Expression<Func<Customer, bool>>时,你说你的方法接受一个代码树,它由编译器从C#代码转换而来。

由于您已经拥有代码树,因此必须将其直接传递给Where()方法,而不是使用LINQ语法。

答案 2 :(得分:0)

不同之处在于,在sql-like中,它期望表达式计算为bool。但在Where方法中,表达式的类型可以是委托。

要强制第二个工作,您可以更改为whereClause.Compile()(c)或将参数更改为Func<Company, bool> whereClausewhereClause(c)