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
Why Func<T,bool> instead of Predicate<T>?
提问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 Queryable
and Enumerable
methods and extension methods... what's up with that?
为什么他们使用Func<TSource, bool>
而不是Predicate<TSource>
?似乎Predicate<TSource>
只有List<T>
and 使用Array<T>
,而Func<TSource, bool>
几乎所有Queryable
andEnumerable
方法和扩展方法都使用了...这是怎么回事?
采纳答案by Jb Evain
While Predicate
has been introduced at the same time that List<T>
and Array<T>
, in .net 2.0, the different Func
and Action
variants come from .net 3.5.
虽然在 .net 2.0 中Predicate
同时引入了List<T>
和Array<T>
,但不同的Func
和Action
变体来自 .net 3.5。
So those Func
predicates 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<>
andExpression<>
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 T
and 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 Func
instead 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 out
or ref
parameters, but those are less commonly used.
它并不能解决所有问题,因为Func
(and Action
) 不能有out
orref
参数,但这些参数不太常用。
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 Main
function. The compiler silently generates code to new a delegate object that forwards on to the method. So in my Main
function, I could change x1
to be of type ExceptionHandler2
without 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 IsNegative
is a perfectly good thing to assign to the p
and f
variables, as long as I do so directly. But then I can't assign one of those variables to the other.
我的方法IsNegative
非常适合分配给p
和f
变量,只要我直接这样做。但是我不能将这些变量中的一个分配给另一个。