【问题标题】:How to rewrite expression x=>!x to x=>x!=true and x=>x to x=>x==true如何将表达式 x=>!x 重写为 x=>x!=true 和 x=>x 到 x=>x==true
【发布时间】:2014-01-27 13:30:17
【问题描述】:

假设我们这样表达:

someIQueryable.Where(x => x.SomeBoolProperty)
someIQueryable.Where(x => !x.SomeBoolProperty)

我需要将上面的表达式(使用表达式访问者重写)转换为这样的表达式:

someIQueryable.Where(x => x.SomeBoolProperty == true)
someIQueryable.Where(x => x.SomeBoolProperty != true)

注意:如果我们有更复杂的表达式,重写器也必须在更一般的情况下工作:

 someIQueryable.Where((x => x.SomeBoolProperty && x.SomeIntProperty > 0) || !x.SomeOtherBoolProperty))

【问题讨论】:

  • 您只是要求一个完整的、有效的解决方案吗?你试过什么?
  • 你想达到什么目的?区别在哪里?它将被翻译成相同的查询。
  • 你为什么要这样做?
  • 是的,我正在将其转换为 SQL 查询。但不幸的是,当我翻译它们时,结果查询无效
  • @Gor 就个人而言,我会说“修复你的提供商”;p

标签: c# linq expression expressionvisitor


【解决方案1】:

类似:

static class BooleanComplexifier
{
    public static Expression<T> Process<T>(Expression<T> expression)
        where T : class
    {
        var body = expression.Body;
        if (body.Type == typeof(bool))
        {
            switch(body.NodeType)
            {
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                    return expression;
                case ExpressionType.Not:
                    body = Expression.NotEqual(
                        ((UnaryExpression)body).Operand,
                        Expression.Constant(true));
                    break;
                default:
                        body = Expression.Equal(body,
                        Expression.Constant(true));
                    break;
            }
            return Expression.Lambda<T>(body, expression.Parameters);
        }
        return expression;
    }   
}

与:

Expression<Func<Foo, bool>> x = foo => foo.IsAlive,
    y = foo => !foo.IsAlive;

var a = BooleanComplexifier.Process(x); // foo => foo.IsAlive == true
var b = BooleanComplexifier.Process(y); // foo => foo.IsAlive != true
//...
class Foo
{
    public bool IsAlive { get;set; }
}

对于更复杂的处理,ExpressionVisitor 可能是必要的:

class BooleanComplexifier : ExpressionVisitor
{
    public static Expression<T> Process<T>(Expression<T> expression)
    {
        return (Expression<T>)new BooleanComplexifier().Visit(expression);
    }

    int bypass;
    protected override Expression VisitBinary(BinaryExpression node)
    {
        if (bypass == 0 && node.Type == typeof(bool))
        {
            switch (node.NodeType)
            {
                case ExpressionType.And: // bitwise & - different to &&
                case ExpressionType.Or: // bitwise | - different to ||
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                    bypass++;
                    var result = base.VisitBinary(node);
                    bypass--;
                    return result;
            }
        }
        return base.VisitBinary(node);
    }
    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (bypass == 0 && node.Type == typeof(bool))
        {
            switch(node.NodeType)
            {
                case ExpressionType.Not:
                    bypass++;
                    var result = Expression.NotEqual(
                        base.Visit(node.Operand),
                        Expression.Constant(true));
                    bypass--;
                    return result;
            }
        }
        return base.VisitUnary(node);
    }
    protected override Expression VisitMember(MemberExpression node)
    {
        if(bypass == 0 && node.Type == typeof(bool))
        {
            return Expression.Equal(
                base.VisitMember(node),
                Expression.Constant(true));
        }
        return base.VisitMember(node);
    }
}

【讨论】:

  • 呵呵,这个班好听的名字:)
  • 这在 z = foo => foo.IsAlive && !foo.IsAlive 上失败。 // 它产生 foo => ((foo.IsAlive && Not(foo.IsAlive)) == True)
  • @Gor 确实如此,但问题并未将其指定为要求;p 如果场景是特别任何对不涉及的布尔属性的访问!= / ==,那么这可以完成 - 但需要不同的方法:2 分钟...
  • 是的。这就是我写笔记的原因。实际上,问题是,如何检测这种情况。一种方法是检查父表达式是否为二进制布尔表达式。但这会导致查找父表达式的问题。
  • @GorRustamyan 如果您需要完全控制,则必须保留父母的Stack&lt;Expression&gt;,推送和弹出(大概在Visit 方法中),这样您就可以检查整个祖先
猜你喜欢
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多