为什么 Func而不是谓词

发布于 2024-07-15 13:09:42 字数 749 浏览 13 评论 0原文

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

在 .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 代替谓词? 似乎 Predicate 仅由 ListArray 使用,而 Func 几乎被所有 QueryableEnumerable 方法和扩展方法使用......这是怎么回事?

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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

筱果果 2024-07-22 13:09:42

虽然 PredicateListArray 同时引入,但在 .net 2.0 中,不同的 FuncAction 变体来自 .net 3.5。

因此,这些 Func 谓词主要用于 LINQ 运算符的一致性。 从 .net 3.5 开始,关于使用 FuncAction 指南状态

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

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.

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:

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

飞烟轻若梦 2024-07-22 13:09:42

我以前也想过这个问题。 我喜欢 Predicate 委托 - 它很好且具有描述性。 但是,您需要考虑 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>)

不会。

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:

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.

香橙ぽ 2024-07-22 13:09:42

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

尽管 FuncPredicate 都具有相同的参数和返回类型,但它们并不兼容赋值。 因此,如果每个库都为每个委托模式声明了自己的委托类型,那么这些库将无法互操作,除非用户插入“桥接”委托来执行转换。

    // 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)不能有 outref 参数,但这些不太常用。

更新: Svish 在评论中说道:

仍然可以从以下位置切换参数类型
函数到谓词和
回来了,好像没做什么
不同之处? 至少它仍然可以编译
没有任何问题。

是的,只要您的程序仅将方法分配给委托,如我的 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 非常适合分配给 pf 变量,只要我这样做就这么直接。 但是我无法将其中一个变量分配给另一个变量。

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

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.

    // 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.

It doesn't solve all problems, because Func (and Action) can't have out or ref parameters, but those are less commonly used.

Update: in the comments Svish says:

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.

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.

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'.

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.

迷路的信 2024-07-22 13:09:42

建议(在 3.5 及以上版本中)是使用 Action<...>Func<...> - 来回答“为什么?” - 一个优点是“Predicate”仅在您知道“predicate”含义时才有意义 - 否则您需要查看对象浏览器(等)来查找签名。

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

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

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文