C# 重构 lambda 表达式

发布于 2024-08-05 05:12:21 字数 774 浏览 2 评论 0 原文

我有几个共享属性的 Expression> 表达式。例如,

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>(BinaryExpression.AndAlso( 等...) 构建表达式来做到这一点,但它并没有简化我的代码,而是使读。

I have several Expression<Func<User,bool>> expressions that shares properties. For example,

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";

Is there an easy way to put the u.IsActive && u.Group != "PROCESS" in a variable and use it in e1, e2 and e3? Edited : And I still want the same tree.

Seems I can do it by building the expression with Expression.Lambda<Func<User, bool>>(BinaryExpression.AndAlso( etc... But rather than simplifying my code it made it more difficult to read.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不顾 2024-08-12 05:12:21

我相信没有更干净的方法可以为您的案例做到这一点。您可以使用 BinaryExpression 正如您所提到的。您可以将 BinaryExpressionExpression.Lambda 调用封装到一个方法中并调用该方法(例如 PredicateBuilder.And),但在我看来,这些都不像当前语法那么干净。

I believe there's no cleaner way to do that for your case. You can use BinaryExpression as you mentioned. You can encapsulate the BinaryExpression and Expression.Lambda calls into a method and call that instead (like PredicateBuilder.And) but none of those are as clean as the current syntax IMO.

一萌ing 2024-08-12 05:12:21

lambda 表达式的问题在于它们是不可变的,并且您无法轻松替换 lambda 的参数。我最初的想法是做这样的事情(不幸的是,这不会成功):

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");

Problem with lambda expressions is that they are immutable and you can't easily replace parameters of lambda. My original idea was to do something like this (unfortunately, this won't work out):

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()
    }
}

and use in code:

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");

Possible solution

You can try to use one of the project aiming on implementing expression builders. I haven't used any of them but google gives plenty of links, for example:

Another approach

If you are using these expressions in LINQ to filter values, you can user different approach (don't combine expressions but combine filters):

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");
长不大的小祸害 2024-08-12 05:12:21

我认为不一定有比您已经使用的答案更好的答案。正如 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

...但是这是否有任何用途将取决于您对已编译的委托。可能对你来说完全没用,但值得一提以防万一!

I don't think that there's necessarily a better answer than the one which you're using already. As Mehrdad mentions, you'll have to build a deeper tree using a BinaryExpression and I think that would be a step backwards in readability from your current code.

Depending on your usage, you might be able to save some lines of code by exploiting closure semantics and doing something like this:

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

...but whether that's any use will depend on what you're doing with the compiled delegate. May be entirely useless to you but worth a mention just in case!

陌生 2024-08-12 05:12:21
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";
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";
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文