これは単なる好奇心の質問であり、誰かに良い答えがあったかどうか疑問に思っていました:
.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>
はほとんどすべてのQueryable
およびEnumerable
メソッドで使用されるようですおよび拡張メソッド...それは何ですか?
Predicate
はList<T>
およびArray<T>
と同時に導入されましたが、.net 2.0では、異なるFunc
およびAction
バリアントが.net 3.5から導入されました。
したがって、これらのFunc
述語は、主にLINQ演算子の一貫性のために使用されます。 .net 3.5以降、Func<T>
およびAction<T>
の使用について ガイドラインの状態 :
カスタムデリゲートおよび述語の代わりに、新しいLINQ型
Func<>
およびExpression<>
を使用してください
私はこれを以前に疑問に思いました。私は好き 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>)
ありません。
確かに、特定のデリゲートの代わりに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
パラメーターを持つことはできないため、すべての問題を解決するわけではありませんが、これらはあまり一般的ではありません。
更新: 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
変数に割り当てるのに最適です。しかし、それらの変数の一方を他方に割り当てることはできません。
アドバイス(3.5以降)では、Action<...>
およびFunc<...>
-「なぜ?」 -1つの利点は、「Predicate<T>
"は、"述語 "の意味を知っている場合にのみ意味があります。そうでない場合は、オブジェクトブラウザ(など)を見て署名を見つける必要があります。
逆にFunc<T,bool>
は標準パターンに従います。これはT
を取り、bool
を返す関数であることをすぐに知ることができます-用語を理解する必要はありません-私の真実のテストを適用してください。
「述語」の場合、これは問題ないかもしれませんが、標準化の試みに感謝します。また、その領域の関連するメソッドと同等の多くのことができます。