ラムダ式をリファクタリングするC#
-
10-07-2019 - |
質問
プロパティを共有するいくつかのExpression<Func<User,bool>>
式があります。たとえば、
Expression<Func<User, bool>> e1 =
(User u) => u.IsActive && u.Group != "PROCESS" && u.Name != null;
Expression<Func<User, bool>> e2 =
(User u) => u.IsActive && u.Group != "PROCESS" && u.Name != "A";
Expression<Func<User, bool>> e3 =
(User u) => u.IsActive && u.Group != "PROCESS" && u.Name != "B";
u.IsActive && u.Group != "PROCESS"
を変数に入れてe1、e2、e3で使用する簡単な方法はありますか?編集済み:そして、私はまだ同じツリーが必要です。
Expression.Lambda<Func<User, bool>>(BinaryExpression.AndAlso(
などを使用して式を作成することでそれができるようです。しかし、コードを単純化するよりも読みにくくなりました。
解決
あなたの場合、それを行うよりクリーンな方法はないと思います。前述のようにBinaryExpression
を使用できます。 Expression.Lambda
および<=>呼び出しをメソッドにカプセル化し、代わりに呼び出すことができます( PredicateBuilderなど)そして)しかし、それらのどれも現在の構文IMOほどきれいではありません。
他のヒント
ラムダ式の問題は、ラムダ式が不変であり、ラムダのパラメータを簡単に置き換えることができないことです。私の当初のアイデアは、このようなことをすることでした(残念ながら、これはうまくいきません):
public static class ExpressionExtesions
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> baseCondition, Expression<Func<T, bool>> additionalCondition)
{
var and = Expression.AndAlso(baseCondition.Body, additionalCondition.Body);
return Expression.Lambda<Func<T, bool>>(and, baseCondition.Parameters); // additionalCondition.Body still uses its own parameters so this fails on Compile()
}
}
およびコードで使用:
Expression<Func<User, bool>> e = usr => usr.IsActive && usr.Group != "PROCESS";
var e1 = e.And(u => u.Name != null);
var e2 = e.And(u => u.Name != "A");
var e3 = e.And(u => u.Name != "B");
可能な解決策
式ビルダーの実装を目的としたプロジェクトのいずれかを使用してみてください。私はそれらのどれも使用していませんが、グーグルは多くのリンクを提供します、例えば:
別のアプローチ
LINQでこれらの式を使用して値をフィルター処理する場合、別のアプローチを使用できます(式を結合しないでフィルターを結合します):
var activeUsers = allUsers.Where(usr => usr.IsActive && usr.Group != "PROCESS");
var usersAll = activeUsers.Where(u => u.Name != null);
var usersNotA = activeUsers.Where(u => u.Name != "A");
var usersNotB = activeUsers.Where(u => u.Name != "B");
すでに使用しているものよりも必ずしも良い答えがあるとは思わない。 Mehrdadが言及しているように、BinaryExpressionを使用してより深いツリーを構築する必要がありますが、それは現在のコードから読みやすさを後退させると思います。
使用方法に応じて、クロージャのセマンティクスを活用して次のような操作を行うことで、コードを数行保存できる可能性があります
string name = null;
Expression<Func<User, bool>> e =
(User u) => u.IsActive && u.Group != "PROCESS" && u.Name != name;
var expr = e.Compile();
name = "A";
var result = expr.Invoke(u); //True (assume u.Name = "B")
name = "B";
result = expr.Invoke(u); //False
...しかし、それが使用されるかどうかは、コンパイルされたデリゲートで何をしているかに依存します。あなたにはまったく役に立たないかもしれませんが、念のために言及する価値があります!
var test = new Func<User, bool>(u=> u.IsActive && u.Group != "PROCESS");
Expression<Func<User, bool>> e1 = (User u) => test(u) && u.Name != null;
Expression<Func<User, bool>> e2 = (User u) => test(u) && u.Name != "A";
Expression<Func<User, bool>> e3 = (User u) => test(u) && u.Name != "B";