C# 为什么 Func<T,bool> 而不是 Predicate<T>?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/665494/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 12:28:58  来源:igfitidea点击:

Why Func<T,bool> instead of Predicate<T>?

c#.netpredicatefunc

提问by Svish

This is just a curiosity question I was wondering if anyone had a good answer to:

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

In the .NET Framework Class Library we have for example these two methods:

例如,在 .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
)

Why do they use Func<TSource, bool>instead of Predicate<TSource>? Seems like the Predicate<TSource>is only used by List<T>and Array<T>, while Func<TSource, bool>is used by pretty much all Queryableand Enumerablemethods and extension methods... what's up with that?

为什么他们使用Func<TSource, bool>而不是Predicate<TSource>?似乎Predicate<TSource>只有List<T>and 使用Array<T>,而Func<TSource, bool>几乎所有QueryableandEnumerable方法和扩展方法都使用了...这是怎么回事?

采纳答案by Jb Evain

While Predicatehas been introduced at the same time that List<T>and Array<T>, in .net 2.0, the different Funcand Actionvariants come from .net 3.5.

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

So those Funcpredicates are used mainly for consistency in the LINQ operators. As of .net 3.5, about using Func<T>and Action<T>the guideline states:

所以这些Func谓词主要用于 LINQ 运算符的一致性。作为.NET 3.5,有关使用的Func<T>Action<T>方针状态

Do use the new LINQ types Func<>and Expression<>instead of custom delegates and predicates

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

回答by Jon Skeet

I've wondered this before. I like the Predicate<T>delegate - it's nice and descriptive. However, you need to consider the overloads of Where:

我以前也想过这个。我喜欢Predicate<T>代表 - 它很好且具有描述性。但是,您需要考虑 的重载Where

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

That allows you to filter based on the index of the entry as well. That's nice and consistent, whereas:

这也允许您根据条目的索引进行过滤。这很好且一致,而:

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

wouldn't be.

不会。

回答by Marc Gravell

The advice (in 3.5 and above) is to use the Action<...>and Func<...>- for the "why?" - one advantage is that "Predicate<T>" is only meaningful if you know what "predicate" means - otherwise you need to look at object-browser (etc) to find the signatute.

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

Conversely Func<T,bool>follows a standard pattern; I can immediately tell that this is a function that takes a Tand returns a bool- don't need to understand any terminology - just apply my truth test.

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

For "predicate" this might have been OK, but I appreciate the attempt to standardise. It also allows a lot of parity with the related methods in that area.

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

回答by Daniel Earwicker

Surely the actual reason for using Funcinstead of a specific delegate is that C# treats separately declared delegates as totally different types.

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

Even though Func<int, bool>and Predicate<int>both have identical argument and return types, they are not assignment-compatible. So if every library declared its own delegate type for each delegate pattern, those libraries would not be able to interoperate unless the user inserts "bridging" delegates to perform conversions.

尽管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
    }

By encouraging everyone to use Func, Microsoft is hoping that this will alleviate the problem of incompatible delegate types. Everyone's delegates will play nicely together, because they will just be matched up based on their parameter/return types.

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

It doesn't solve all problems, because Func(and Action) can't have outor refparameters, but those are less commonly used.

它并不能解决所有问题,因为Func(and Action) 不能有outorref参数,但这些参数不太常用。

Update:in the comments Svish says:

更新:在评论中 Svish 说:

Still, switching a parameter type from Func to Predicate and back, doesn't seem to make any difference? At least it still compiles without any problems.

尽管如此,将参数类型从 Func 切换到 Predicate 并返回,似乎没有任何区别?至少它仍然编译没有任何问题。

Yes, as long as your program only assigns methods to delegates, as in the first line of my Mainfunction. The compiler silently generates code to new a delegate object that forwards on to the method. So in my Mainfunction, I could change x1to be of type ExceptionHandler2without causing a problem.

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

However, on the second line I try to assign the first delegate to another delegate. Even thought that 2nd delegate type has exactly the same parameter and return types, the compiler gives error CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'.

但是,在第二行,我尝试将第一个委托分配给另一个委托。即使认为第二个委托类型具有完全相同的参数和返回类型,编译器也会给出错误CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'

Maybe this will make it clearer:

也许这会让它更清楚:

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
}

My method IsNegativeis a perfectly good thing to assign to the pand fvariables, as long as I do so directly. But then I can't assign one of those variables to the other.

我的方法IsNegative非常适合分配给pf变量,只要我直接这样做。但是我不能将这些变量中的一个分配给另一个。