なぜPredicate <T>ではなくFunc <T、bool>なのですか?


210

これは好奇心の問いにすぎません。

.NET Frameworkクラスライブラリには、たとえば次の2つのメソッドがあります。

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>ほとんどすべてで使用されているQueryableEnumerableメソッドと拡張メソッドそれに何次第ですか...?


21
ええ、そうです、これらの一貫性のない使用法も私を狂わせます。
ジョージマウアー

回答:


170

ながらPredicateと同時に導入されているList<T>Array<T>、.NET 2.0で、異なるFuncAction変異体は、.NET 3.5から来ます。

したがって、これらのFunc述語は、主にLINQ演算子の一貫性のために使用されます。.NET 3.5の時点で、使用についてFunc<T>およびガイドラインの状態Action<T>

カスタムのデリゲートと述語の代わりに、新しいLINQタイプFunc<>を 使用してくださいExpression<>


7
かっこいい、これまでにこれらのガイドラインを見たことがない=)
Svish

6
ほとんどの投票があるので、これを回答として受け入れます。そしてジョン・スキートはたくさんの担当者を持っているので...:p
Svish

4
書かれているようにそれは奇妙なガイドラインです。確かに、「新しいLINQタイプ "Func <>"と "Action <>" [...]を使用してください」と記述されているはずです。Expression <>は完全に異なるものです。
ジョンスキート

3
まあ、実際には、それをLINQプロバイダーの作成に関するガイドラインのコンテキストに配置する必要があります。ええ、LINQオペレーターの場合、ガイドラインは拡張メソッドのパラメーターとしてFunc <>とExpression <>を使用することです。私はのFuncとアクションの使用についての個別のガイドラインをいただければと思います同意
JbのEvain

スキートのポイントは、Expression <>はガイドラインではないということです。これは、その型を認識してそれとは異なる何かを行うC#コンパイラー機能です。
Daniel Earwicker 2009年

116

私は前にこれを疑問に思いました。私は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>)

ないだろう。


11
Predicate <int、bool>はやや醜い-述語は通常(コンピュータサイエンスのIME)、単一の値に基づいています。もちろん、Predicate <Pair <T、int >>になる可能性もありますが、それはさらに醜くなります:)
Jon Skeet

とても本当...へへ。いいえ、Funcの方がきれいだと思います。
Svish

2
ガイドラインのため、他の回答を受け入れました。ただし、複数の回答にマークを付けたいと思います。いくつかの回答を「非常に注目に値する」または「回答に加えて注意すべき優れた点もある」としてマークを付けることができればいいのですが
Svish

6
うーん、最初のコメントを間違って書いただけだ:P私の提案はもちろんPredicate <T、int> ...
Svish

4
@JonSkeet私はこの質問が古くてすべてであることを知っていますが、皮肉なことを知っていますか?Where拡張メソッドのパラメーターの名前はpredicateです。へh = P。
コンラッドクラーク

32

確か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)がoutor refパラメータを持つことができないため、すべての問題を解決するわけではありませんが、それらはあまり一般的に使用されません。

更新:コメントでSvishは言う:

それでも、パラメータタイプをFuncからPredicateに切り替えてから戻しても、違いはないように見えますか?少なくとも問題なくコンパイルできます。

はい、Main関数の最初の行のように、プログラムがデリゲートにメソッドを割り当てるだけである限り。コンパイラは、メソッドに転送する新しいデリゲートオブジェクトへのコードをサイレントに生成します。したがって、私のMain関数では、問題を起こさずx1にタイプに変更できますExceptionHandler2

ただし、2行目では、最初のデリゲートを別のデリゲートに割り当てようとしています。2番目のデリゲート型はパラメーターと戻り値の型がまったく同じであると考えていても、コンパイラーはエラーを出します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変数に割り当てるのに最適です。しかし、それらの変数の1つを他の変数に割り当てることはできません。


それでも、パラメータタイプをFunc <T、bool>からPredicate <T>に切り替えてから戻しても、違いはないように見えますか?少なくとも問題なくコンパイルできます。
Svish

MSは、開発者がこれらが同じであると考えないようにしようとしているようです。なんでかしら?
Matt Kocaj

1
パラメータタイプを切り替えると、それはどちらかと型付けされますので、あなたはそれに渡している式は、メソッド呼び出しに別々に定義されている場合の違いを確認しFunc<T, bool>たりPredicate<T>ではなく、コンパイラによって推論型を有します。
Adam Ralph

30

(3.5以上)でアドバイスを使用することですAction<...>し、Func<...>「なぜ?」のために-を Predicate<T>-1 つの利点は、「」が意味するのは「述語」が何を意味するかを知っている場合にのみ意味があるということです。

逆にFunc<T,bool>標準パターンに従います。これはaを受け取り、a Tを返す関数であることがすぐboolにわかります。用語を理解する必要はありません。ただ真実テストを適用してください。

「述語」の場合、これは問題なかったかもしれませんが、標準化の試みに感謝します。また、その領域の関連するメソッドと多くの同等性を可能にします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.