这只是一个好奇的问题,我想知道是否有人有一个很好的答案:
在.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
方法和扩展程序方法......那是什么?
答案 0 :(得分:158)
虽然Predicate
与List<T>
同时引入Array<T>
,但在.net 2.0中,不同的Func
和Action
变体来自.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
)不能有out
或ref
个参数,但这些参数不太常用。< / 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
是分配给p
和f
变量的完美好处,只要我直接这样做。但后来我无法将其中一个变量分配给另一个。
答案 3 :(得分:27)
建议(3.5及以上)是使用Action<...>
和Func<...>
- “为什么?” - 一个优点是“Predicate<T>
”只有在您知道“谓词”的含义时才有意义 - 否则您需要查看对象浏览器(等)以找到签名。
相反Func<T,bool>
遵循标准模式;我可以立即告诉我这是一个需要T
并返回bool
的函数 - 不需要理解任何术语 - 只需应用我的真值测试。
对于“谓词”,这可能没问题,但我很欣赏标准化的尝试。它还允许与该领域的相关方法进行大量的平等。