这只是一个好奇的问题,我想知道是否有人有一个好的答案:

在.NET框架类库中,我们有这样两个方法:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

为什么他们使用Func<TSource, bool>而不是Predicate<TSource>?似乎Predicate<TSource>只被List<T>和Array<T>使用,而Func<TSource, bool>几乎被所有可查询和可枚举方法和扩展方法使用……这是怎么回事?


当前回答

我以前也想过这个问题。我喜欢Predicate<T>委托-它很好,很有描述性。但是,你需要考虑Where的重载:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

这也允许您基于条目的索引进行筛选。这很好,很一致,然而:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

不会。

其他回答

建议(在3.5和以上)是使用Action<…>和Func<…> -“为什么?”-一个优点是“Predicate<T>”只有在你知道“Predicate”的意思时才有意义-否则你需要查看对象浏览器(等)来找到签名。

相反,Func<T,bool>遵循标准模式;我可以立即看出这是一个接受T并返回bool值的函数——不需要理解任何术语——只需要应用我的真值测试。

对于“predicate”,这可能还可以,但我欣赏这种标准化的尝试。它还允许与该领域的相关方法有很多相同之处。

当然,使用Func而不是特定委托的实际原因是c#将单独声明的委托视为完全不同的类型。

尽管Func<int, bool>和Predicate<int>都有相同的参数和返回类型,但它们不兼容赋值。因此,如果每个库都为每个委托模式声明了自己的委托类型,那么这些库将无法互操作,除非用户插入“桥接”委托来执行转换。

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

通过鼓励每个人都使用Func,微软希望这将缓解不兼容的委托类型问题。每个委托都能很好地发挥作用,因为它们只是根据参数/返回类型进行匹配。

它不能解决所有问题,因为Func(和Action)不能有out或ref参数,但这些不太常用。

更新:在评论中Svish说:

不过,从 Func到Predicate和 回来了,好像什么都没有 不同吗?至少它仍然可以编译 没有任何问题。

是的,只要你的程序只把方法分配给委托,就像Main函数的第一行一样。编译器无声地生成代码以新建一个转发到方法的委托对象。因此,在Main函数中,我可以将x1更改为ExceptionHandler2类型,而不会引起问题。

但是,在第二行中,我尝试将第一个委托分配给另一个委托。即使第二个委托类型具有完全相同的参数和返回类型,编译器也会给出错误CS0029:不能隐式地将类型'ExceptionHandler1'转换为'ExceptionHandler2'。

也许这能让你更清楚:

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

我的方法IsNegative是一个分配给p和f变量的完美的东西,只要我直接这样做。但我不能把其中一个变量赋值给另一个。

我以前也想过这个问题。我喜欢Predicate<T>委托-它很好,很有描述性。但是,你需要考虑Where的重载:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

这也允许您基于条目的索引进行筛选。这很好,很一致,然而:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

不会。

在。net 2.0中,Predicate与List<T>和Array<T>同时引入,不同的Func和Action变体来自。net 3.5。

这些Func谓词主要用于LINQ操作符的一致性。从。net 3.5开始,关于使用Func<T>和Action<T>,指南中写道:

是否使用新的LINQ类型Func<>和 表达式<>而不是custom 委托和谓词