为什么`Predicate `与`Func `不匹配?
我尝试在C#中编译以下代码:
public static T FirstEffective(IEnumerable list) { Predicate pred = x => x != null; return Enumerable.FirstOrDefault(list, pred); }
编译器(Mono / .NET 4.0)给出以下错误:
File.cs(139,47) The best overloaded method match for `System.Linq.Enumerable.FirstOrDefault(this System.Collections.Generic.IEnumerable,System.Func)' has some invalid arguments /usr/lib/mono/4.0/System.Core.dll (Location of the symbol related to previous error) File.cs(139,47): error CS1503: Argument `#2' cannot convert `System.Predicate' expression to type `System.Func'
这是相当奇怪的,因为Predicate
实际上是一个函数,它将参数T
作为输入并返回一个bool
( T
甚至是“协变”,因此允许T
的特化)。 代表们是否不考虑“利斯科夫替代原则”来推导出Predicate
相当于Func
? 据我所知,这个等价问题应该是可判定的。
C#规范很清楚:
15.1代表声明
C#中的委托类型是名称等价的,在结构上不等同。 具体而言, 具有相同参数列表和返回类型的两种不同委托类型被视为不同的委托类型 。
这就是你的代码无法编译的原因。
您可以通过调用委托来使其工作,而不是传递它:
public static T FirstEffective (IEnumerable list) { Predicate pred = x => x != null; return Enumerable.FirstOrDefault (list, x => pred(x)); }
更新
Eric Lippert撰写了一篇很棒的博客文章:C#Team的前成员,微软,它详细回答了你的问题: 代表和结构身份 。
委托类型不可隐式转换,即使它们具有所有相同的参数和返回信息。 但是,您的案例有一个简单的解决方法。 您可以在委托实例上使用.Invoke
方法。
public static T FirstEffective(IEnumerable list) { Predicate pred = x => x != null; return Enumerable.FirstOrDefault(list, pred.Invoke); }
至于为什么代表们以这种方式工作的问题,答案是这是一个设计决策。 具有相同公共接口的类也不可隐式转换,因此它并不真正不一致。
相当姗姗来迟,但巧合的是我偶然发现了同样的问题,可以在这里找到准确的答案: 重要评论
基本上它意味着它是基于不幸的决定以这种方式实现它的不一致。 虽然predicate
,但它们是不同的类型,尽管签名相同。 我认为出于向后兼容性的原因,可以转换表达式和/或lambda,然后通过new predicate
返回谓词。