【问题标题】:Why are lambda expression arguments ambiguous between Func and Expression<Func>?为什么 lambda 表达式参数在 Func 和 Expression<Func> 之间不明确?
【发布时间】:2012-01-07 00:30:48
【问题描述】:

假设我有一堂课:

class MyClass {
    public int MyMethod(Func<int, int> f) { return 0; }
    public int MyMethod(Expression<Func<int, int>> f) { return 1; }
}

当我尝试使用 lambda 表达式调用该方法时,我收到一个编译错误,指出两个重载之间的调用不明确:

var myClass = new MyClass();
myClass.MyMethod(x => 1 + x); // Error!

当然,使用显式类型调用可以正常工作:

myClass.MyMethod((Func<int, int>)(x => 1 + x)); // OK, returns 0
myClass.MyMethod((Expression<Func<int, int>>)(x => 1 + x)); // OK, returns 1

表达式树包含更多信息(实际代码),我可能希望在可用时利用这些信息。但我也希望我的代码与代表一起工作。不幸的是,这种模棱两可造成了这种情况,所以我必须找到另一种方法来区分这两个调用,这会弄乱原本干净的 API。

C# 规范没有说明这种特定情况,所以从这个意义上说,行为确实符合规范。

但是,有一个论点是表达式树应该优先于委托。 Compile 方法充当从表达式树到委托的显式转换。表达式树包含更多信息,当您编译为委托时,您会丢失该信息。另一个方向没有转换。

有什么理由不喜欢表达式树吗?

【问题讨论】:

  • 支持Func的论点是,您可以直接执行它,而无需经过相对昂贵的编译阶段。

标签: .net lambda expression-trees


【解决方案1】:

回答标题上的问题,它们是模棱两可的,因为类型系统没有“lambda 表达式”的概念。这是一个编译器功能,可以转换为委托或表达式树,因此您需要明确要转换为哪种类型。大多数情况下,由于使用 lambda 表达式的上下文,编译器也会自动推断目标。例如,在 IEnumerable 扩展方法中使用 lambda 与在 IQueryable 扩展方法中使用 lambda。

现在,要回答为什么不总是更喜欢表达式树的问题,您有 MagnatLU 已经陈述的性能论点。如果您接受Expression,然后调用Compile 来执行它,那么与接受委托相比,它总是会更慢。

两者在语义上也有区别,委托只是执行某些代码的一种方式,而表达式树是对实际代码的描述。

如果我是你,我会选择将接受表达式的方法的名称更改为清楚地反映它对基于委托的方法所做的额外操作。

【讨论】:

  • 一个 lambda 表达式可以用两种方式解释这一事实本身并不意味着不存在优先于另一种的规则。所以我不太喜欢你回答的那部分。不过,其余的都很棒。谢谢!
【解决方案2】:

编译器如何知道是选择表达式树还是委托?这是您的决定,而不是编译器的决定。

所有 linq 方法也提供委托和表达式,但它们是目标类型形式的扩展。

 Enumerable.Where<T>(this IEnumerable<T> en , Func<T,bool> filter)
 Queryable.Where<T>(this IQueryable<T> en , Expression<Func<T,bool>> filter)

所以编译器会根据目标类型做出选择。编译器是一个程序,实际上它是机器并且它是上下文无关的,它不能像人类那样决定什么可能更好,它只遵循规则并且这些规则需要是明确的。

【讨论】:

  • 问题不在于为什么编译器将其标记为模棱两可:那是因为它在规范中被遗漏了。问题是为什么语言设计者让它模棱两可。
  • 为什么没有具体的答案,因为每个排列组合都不可能设计,这样就会有很多这样的低优先级和罕见使用场景的规则。猜测和添加所有设计规则是不切实际的。
【解决方案3】:

构建和编译表达式树需要时间,并且会给 GC 带来很大压力。你不应该在运行时构造和编译表达式,除非你真的必须这样做。

编辑:另外请记住,并非每个表达式或方法都可以表示为Expression&lt;E&gt;Func&lt;U, V&gt; 只关心参数和返回类型,没有这些限制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-26
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 2010-10-22
    相关资源
    最近更新 更多