218

これは好奇心の質問であり、誰かが次のことに対して良い答えを持っているかどうか疑問に思っていました。

.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メソッドと拡張メソッドによって使用されています...どうしたのですか?

4

4 に答える 4

176

はandPredicateと同時に導入されましたが、.net 2.0 では、.net 3.5 から異なるおよびバリアントが導入されました。List<T>Array<T>FuncAction

そのため、これらのFunc述語は主に LINQ 演算子の一貫性のために使用されます。.net 3.5以降、使用Func<T>ガイドラインAction<T>の状態について:

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

于 2009-03-20T09:47:46.803 に答える
118

これは以前から疑問に思っていました。私は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>)

そうではないでしょう。

于 2009-03-20T09:51:25.827 に答える
35

確かに、特定のデリゲートの代わりに使用する実際の理由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(and Action) はoutorパラメーターを持つことができないため、すべての問題を解決できるわけではありませんがref、これらはあまり一般的に使用されていません。

更新:コメントで Svish は言います:

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

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

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

于 2009-03-20T10:12:05.343 に答える
32

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

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

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

于 2009-03-20T09:56:34.367 に答える