为什么`Predicate <t>`不匹配`Func <t,bool>`?</t,bool> </t>

时间:2014-08-25 03:45:11

标签: c# function delegates predicate

我尝试在C#中编译以下代码:

public static T FirstEffective(IEnumerable<T> list) 
{
    Predicate<T> 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<T>(this System.Collections.Generic.IEnumerable<T>,System.Func<T,bool>)' 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<T>' expression to type `System.Func<T,bool>'

这很奇怪,因为Predicate<T>实际上是一个函数,它将参数T作为输入并返回boolT是偶数&#34;协变&#34;因此允许T的特化。代表们不采取&#34; Liskov替代原则&#34;考虑到导出Predicate<T>等同于Func<T,bool>?据我所知,这个等价问题应该是可判定的。

3 个答案:

答案 0 :(得分:16)

C#规范很清楚:

  

15.1委托声明

     

C#中的委托类型是名称等价的,在结构上不等同。   具体来说,具有相同的两种不同委托类型   参数列表和返回类型被视为不同的委托   类型

这就是你的代码无法编译的原因。

您可以通过调用委托来使其工作,而不是传递它:

public static T FirstEffective (IEnumerable<T> list) {
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault (list, x => pred(x));
}

<强>更新

Eric Lippert撰写了一篇很棒的博客文章:C#Team的前成员为Microsoft,它详细解答了您的问题:Delegates and structural identity

答案 1 :(得分:4)

委托类型不可隐式转换,即使它们具有所有相同的参数和返回信息。但是,您的案例有一个简单的解决方法。您可以在委托实例上使用.Invoke方法。

public static T FirstEffective<T>(IEnumerable<T> list)
{
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault(list, pred.Invoke);
}

关于代表们为何如此工作的问题,答案是这是一个设计决策。具有相同公共接口的类也不可隐式转换,因此它实际上并不一致。

答案 2 :(得分:1)

相当迟到但巧合的是我偶然发现了同样的问题,可以在这里找到准确的答案:important comment

基本上,这意味着它是基于不幸的决定以这种方式实现它的不一致。虽然politician虽然有相同的签名,但它们是不同的类型。我认为出于向后兼容性的原因,可以转换表达式和/或lambda,然后通过predicate<T> == func<T, bool>返回谓词。