为什么Func <t,bool>而不是Predicate <t>?</t> </t,bool>

时间:2009-03-20 09:39:31

标签: c# .net predicate func

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

在.NET Framework类库中,我们有两个方法:

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>几乎用于所有QueryableEnumerable方法和扩展程序方法......那是什么?

4 个答案:

答案 0 :(得分:158)

虽然PredicateList<T>同时引入Array<T>,但在.net 2.0中,不同的FuncAction变体来自.net 3.5。

因此,那些Func谓词主要用于LINQ运算符的一致性。从.net 3.5开始,关于使用Func<T>Action<T> guideline states

  

使用新的LINQ类型Func<>和   Expression<>而不是自定义   代表和谓词

答案 1 :(得分:110)

我以前想过这个。我喜欢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>)

不会。

答案 2 :(得分:30)

使用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,Microsoft希望这可以缓解不兼容的委托类型的问题。每个人的代表都会很好地一起玩,因为他们只会根据参数/返回类型进行匹配。

它并不能解决所有问题,因为Func(和Action)不能有outref个参数,但这些参数不太常用。< / p>

更新:在评论中Svish说:

  

仍然,从中切换参数类型   Func to Predicate and   回来,似乎没有做任何   区别?至少它仍然编译   没有任何问题。

是的,只要您的程序只为委托分配方法,就像我的Main函数的第一行一样。编译器以静默方式为新的委托对象生成代码,该委托对象转发给该方法。因此,在我的Main函数中,我可以将x1更改为ExceptionHandler2类型而不会导致问题。

但是,在第二行,我尝试将第一个委托分配给另一个委托。即使第二个委托类型具有完全相同的参数和返回类型,编译器也会给出错误CS0029: Cannot implicitly convert type 'ExceptionHandler1' to '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是分配给pf变量的完美好处,只要我直接这样做。但后来我无法将其中一个变量分配给另一个。

答案 3 :(得分:27)

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

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

对于“谓词”,这可能没问题,但我很欣赏标准化的尝试。它还允许与该领域的相关方法进行大量的平等。