我尝试在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
作为输入并返回bool
(T
是偶数&#34;协变&#34;因此允许T
的特化。代表们不采取&#34; Liskov替代原则&#34;考虑到导出Predicate<T>
等同于Func<T,bool>
?据我所知,这个等价问题应该是可判定的。
答案 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>
返回谓词。