这只是一个好奇的问题,我想知道是否有人有一个好的答案:
在.NET框架类库中,我们有这样两个方法:
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>几乎被所有可查询和可枚举方法和扩展方法使用……这是怎么回事?
当然,使用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,微软希望这将缓解不兼容的委托类型问题。每个委托都能很好地发挥作用,因为它们只是根据参数/返回类型进行匹配。
它不能解决所有问题,因为Func(和Action)不能有out或ref参数,但这些不太常用。
更新:在评论中Svish说:
不过,从
Func到Predicate和
回来了,好像什么都没有
不同吗?至少它仍然可以编译
没有任何问题。
是的,只要你的程序只把方法分配给委托,就像Main函数的第一行一样。编译器无声地生成代码以新建一个转发到方法的委托对象。因此,在Main函数中,我可以将x1更改为ExceptionHandler2类型,而不会引起问题。
但是,在第二行中,我尝试将第一个委托分配给另一个委托。即使第二个委托类型具有完全相同的参数和返回类型,编译器也会给出错误CS0029:不能隐式地将类型'ExceptionHandler1'转换为'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变量的完美的东西,只要我直接这样做。但我不能把其中一个变量赋值给另一个。
当然,使用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,微软希望这将缓解不兼容的委托类型问题。每个委托都能很好地发挥作用,因为它们只是根据参数/返回类型进行匹配。
它不能解决所有问题,因为Func(和Action)不能有out或ref参数,但这些不太常用。
更新:在评论中Svish说:
不过,从
Func到Predicate和
回来了,好像什么都没有
不同吗?至少它仍然可以编译
没有任何问题。
是的,只要你的程序只把方法分配给委托,就像Main函数的第一行一样。编译器无声地生成代码以新建一个转发到方法的委托对象。因此,在Main函数中,我可以将x1更改为ExceptionHandler2类型,而不会引起问题。
但是,在第二行中,我尝试将第一个委托分配给另一个委托。即使第二个委托类型具有完全相同的参数和返回类型,编译器也会给出错误CS0029:不能隐式地将类型'ExceptionHandler1'转换为'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变量的完美的东西,只要我直接这样做。但我不能把其中一个变量赋值给另一个。
我以前也想过这个问题。我喜欢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>)
不会。