为什么`Predicate <T>`与`Func <T,bool>`不匹配?

Wil*_*sem 14 c# delegates predicate function

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

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

编译器(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>'
Run Code Online (Sandbox Code Playgroud)

这是相当奇怪的,因为a Predicate<T>实际上是一个函数,它接受一个参数作为输入T并返回一个bool(T甚至是"协变"因此T允许的特化).代表们是否不考虑"Liskov替代原则"来推导出Predicate<T>相当于Func<T,bool>?据我所知,这个等价问题应该是可判定的.

Mar*_*zek 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));
}
Run Code Online (Sandbox Code Playgroud)

更新

Eric Lippert撰写了一篇很棒的博客文章:C#Team的前成员,微软,它回答了你的问题:代表和结构身份.

  • 这正是方法指针和C#委托之间的区别.它与`class`相同:两个具有相同成员集的类不被认为是等价的.这就是.NET中的类型系统的工作原理. (3认同)
  • 埃里克的博客链接已失效。我能找到的最好的方法是使用 archive.org https://web.archive.org/web/20160331233359/http://blog.coverity.com/2014/06/18/delegates-structural-identity/ (3认同)

rec*_*ive 6

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

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

至于代表为什么要这样工作的问题,答案是这是一个设计决定。具有相同公共接口的类也不能隐式转换,因此它并不是真正不一致。