为什么Func <T,bool>而不是Predicate <T>?
这只是一个好奇的问题,我想知道如果有人有一个很好的答案:
在.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>
几乎被所有的Queryable
和Enumerable
方法和扩展方法使用…这是怎么回事?
虽然Predicate
已经在.net 2.0中同时引入了List<T>
和Array<T>
,但是不同的Func
和Action
变体来自.net 3.5。
所以那些Func
谓词主要用于LINQ操作符的一致性。 从.net 3.5开始,关于使用Func<T>
和Action<T>
的指导原则是 :
请使用新的LINQtypes
Func<>
和Expression<>
来代替自定义委托和谓词
我以前想过这个。 我喜欢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>)
不会的。
当然,使用Func
而不是特定委托的真正原因是C#将单独声明的委托视为完全不同的types。
即使Func<int, bool>
和Predicate<int>
都具有相同的参数和返回types,但它们不是分配兼容的。 因此,如果每个库都为每个委托模式声明自己的委托types,那么除非用户插入“桥接”委托来执行转换,否则这些库将不能互操作。
// 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希望这可以缓解不兼容的委托types问题。 每个人的代表都会一起玩,因为他们只是根据参数/返回types进行匹配。
它并不能解决所有的问题,因为Func
(和Action
)不能有out
参数,但是那些不常用的参数。
更新:在评论Svish说:
不过,从Func切换到Predicate并返回参数types似乎没有什么区别? 至less它仍然编译没有任何问题。
是的,只要你的程序只给委托分配方法,就像我的Main
函数的第一行一样。 编译器以静默方式生成代码给新的委托对象,并转发给方法。 所以在我的Main
函数中,我可以将x1
更改为ExceptionHandler2
types而不会导致问题。
但是,在第二行我尝试将第一个代理分配给另一个代理。 即使认为第二个委托types具有完全相同的参数和返回types,编译器给出错误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
是一个非常好的事情分配给p
和f
variables,只要我直接这样做。 但是我不能把这些variables中的一个指定给另一个。
build议(在3.5及以上版本)是使用Action<...>
和Func<...>
– 为什么? – 一个好处是“ Predicate<T>
”只有在你知道“谓词”是什么意思的时候才有意义 – 否则你需要看对象浏览器(等)来find签名。
相反, Func<T,bool>
遵循标准模式; 我可以立刻知道这是一个函数,需要一个T
并返回一个bool
– 不需要了解任何术语 – 只要应用我的真相testing。
对于“谓词”来说,这可能是确定的,但是我很欣赏标准化的尝试。 这也使得该领域的相关方法有了很大的平衡。