web-dev-qa-db-ja.com

PredicateBuilderの仕組み

簡単に言えば、C#にはPredicateBuilderと呼ばれる無料のクラスがあり、LINQ述語を1つずつ構築します here 。新しい式を述語に追加するメソッドの抜粋を次に示します。誰か説明してもらえますか? (私は この質問 を見ましたが、そのような一般的な答えは望みません。Expression.InvokeとExpression.Lambdaが新しい式を作成する方法の具体的な説明を探しています)。

public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                     Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
        (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}

あなたが持っているとしましょう:

Expression<Func<Person, bool>> isAdult = p1 => p1.Age >= 18;

// I've given the parameter a different name to allow you to differentiate.
Expression<Func<Person, bool>> isMale = p2 => p2.Gender == "Male";

そして、それらをPredicateBuilderと組み合わせます

var isAdultMale = isAdult.And(isMale);

PredicateBuilderが生成するものは、次のような式です。

// Invoke has no direct equivalent in C# lambda expressions.
p1 => p1.Age >= 18 && Invoke(p2 => p2.Gender == "Male", p1)

ご覧のように:

  1. 結果のラムダreuses最初の式のパラメーター。
  2. 最初の式のパラメーターを2番目の式のパラメーターの代わりとして渡すことにより、2番目の式をinvokesする本体を持ちます。結果の InvocationExpression は、メソッド呼び出し(パラメーターの引数を渡すことでルーチンを呼び出す)と同等の式のようなものです。
  3. Andsは最初の式の本体とこのInvocationExpressionを合わせて、結果のラムダの本体を生成します。

考え方は、LINQプロバイダーがこの操作のセマンティクスを理解し、賢明なアクションコースを実行できる必要があるということです(たとえば、WHERE age >= 18 AND gender = 'Male')。

しかし、多くの場合、プロバイダーはInvocationExpressionsに問題があります。これは、「式内のネストされた式呼び出し」の処理の明らかな複雑さのためです。

これを回避するために、LINQKitはExpandヘルパーも提供します。これは、呼び出しをネストされた式のbodyに置き換え、ネストされた式のパラメーターの使用を適切に置き換えて(この場合、p2 with p1)。これにより、次のようになります。

p1 => p1.Age >= 18 && p1.Gender == "Male"

ラムダで自分で行った場合、これらの述語を手動で結合する方法に注意してください。しかし、LINQKitを使用すると、これらの述語独立したソースからを取得して、簡単に結合できます。

  1. 「手で」式コードを書くことなく。
  2. 必要に応じて、結果のラムダのコンシューマに対して透過的な方法で。
46
Ani