C# 为什么是`谓词<;T>;`不';t匹配a`Func<;T、 bool>;`?

C# 为什么是`谓词<;T>;`不';t匹配a`Func<;T、 bool>;`?,c#,function,delegates,predicate,C#,Function,Delegates,Predicate,我尝试用C#编译以下代码: publicstatict FirstEffective(IEnumerable列表) { 谓词pred=x=>x!=null; 返回可枚举的.FirstOrDefault(列表,pred); } 编译器(Mono/.NET 4.0)给出以下错误: File.cs(139,47) The best overloaded method match for `System.Linq.Enumerable.FirstOrDefault<T>(this Syst

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

publicstatict FirstEffective(IEnumerable列表)
{
谓词pred=x=>x!=null;
返回可枚举的.FirstOrDefault(列表,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>'
File.cs(139,47)`System.Linq.Enumerable.FirstOrDefault(this System.Collections.Generic.IEnumerable,System.Func)`的最佳重载方法匹配具有一些无效参数
/usr/lib/mono/4.0/System.Core.dll(与先前错误相关的符号位置)
cs(139,47):错误CS1503:参数`#2'无法将`System.Predicate'表达式转换为`System.Func'类型

这是相当奇怪的,因为
谓词
实际上是一个函数,它接受一个参数
T
作为输入,并返回一个
bool
T
甚至是“协变的”,因此允许对
T
进行专门化)。学员在推导
谓词
等同于
Func
时是否未考虑“Liskov替换原则”?据我所知,这个等价问题应该是可判定的。

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

public static T FirstEffective<T>(IEnumerable<T> list)
{
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault(list, pred.Invoke);
}
publicstatict FirstEffective(IEnumerable列表)
{
谓词pred=x=>x!=null;
返回Enumerable.FirstOrDefault(list,pred.Invoke);
}
至于代表们为什么这样工作的问题,答案是这是一个设计决策。具有相同公共接口的类也不是隐式可转换的,因此也不是真正的不一致。

C#规范明确了这一点:

15.1代表声明

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

这就是你的代码不能编译的原因

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

public static T FirstEffective (IEnumerable<T> list) {
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault (list, x => pred(x));
}
publicstatict FirstEffective(IEnumerable列表){
谓词pred=x=>x!=null;
返回Enumerable.FirstOrDefault(list,x=>pred(x));
}
更新


Eric Lippert写了一篇很棒的博文,他是微软C#Team的前成员,详细回答了你的问题:。

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


基本上,这意味着这是一个不一致的基础上,一个不幸的决定,以这种方式实施它。虽然
谓词==func
,但它们的类型不同,尽管签名相同。我假设出于向后兼容性的原因,可以转换表达式和/或lambda,然后通过
新谓词(func)
返回谓词。请看一下这是相关的,但没有解释为什么不执行等价性检查。如前所述,委托类型的等价性是可判定的……作为你问题的旁白:C#type系统不是图灵完备的,这是事实吗?一个类型系统是图灵完备的,它是不可分的,这是一个要求吗?一般的分型问题是不确定的;你可能会对这篇论文感兴趣:。当它被写成C#时,它还没有逆变,但现在有了逆变,所以关于IL语言的部分适用于C#4.0。@EricLippert:对不起,这些词的确选得不好。非常感谢。这是一个很好的解决方案。但很奇怪的是,代表并不被认为是等价的……是的,但那是因为他们有不同的领域,。。。如何转换它们也不是一件小事。使用数组的
列表
与使用
链接列表
方法的列表完全不同。如果我创建两个类,每个类都只有一个
公共字符串Foo{get;set;}
,即使它们的定义相同,我也无法互换使用它们。我认为这是一个类似的情况。是的@recursive是正确的-这是OOP语言中的常见行为-类/接口/等的名称很重要,而不是签名。@recursive:有一点不同:C#中的编程代码是图灵完整的。因此,无法确定两个类是否相同(当然,可以检查这个简单的示例)。然而,类型系统不是图灵完整的,因此人们可以检查两个签名是否相同……仍然不知道为什么。因为委托之间的转换非常简单:没有真正的转换。相同的“方法指针”就足够了。这正是方法指针和C#委托之间的区别。这与
class
es是一样的:具有相同成员集的两个类不被认为是等价的。这就是.NET中类型系统的工作原理。Eric的博客链接已失效。我能找到的最好的方法就是使用archive.org
public static T FirstEffective (IEnumerable<T> list) {
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault (list, x => pred(x));
}