表达式与运算符&&的组合

本文关键字:组合 运算符 表达式 | 更新日期: 2023-09-27 18:15:44

我有一个类的工作原理有点像Linq To Sql Where子句。

从表达式树中构建一个操作序列。

表达式树是一个Expression<Func<bool>>(即一个没有参数返回bool值的lambda)

conditionBuilder.BuildCondition(() => x != 3 && y != 5);

类工作良好的normal表达式,如上面的例子,但现在我需要的功能,以组合表达式。

我添加了And, Or方法,如

var exp1 = () => x != 3;
var exp2 = () => y != 5;
var exp = ConditionBuilder.And(exp1, exp2);

但是当组合多个表达式时,它会变得复杂。

我想写

var exp = exp1 && exp2;

但是由于我不能直接重载操作符&&我需要找到其他的解决方法。棘手的部分是,结果操作对位操作符没有布尔重载。即exp1 &Exp2是整型而不是bool。(我可以通过添加!= 0来解决这个问题)

我的问题是:

  • 如果我让operator &是一个逻辑表达式(即AndAlso)?
  • 操作符,,如果我超载,它会工作的。/true/false,但这也会创建一个隐式布尔转换。我知道隐式布尔转换是你想在c++中避免的东西,但我不确定它在c#中有什么关系。另外,被覆盖的true和false是否应该对表达式求值?(即if (exp1)应该怎么做?)
编辑:

我已经有了这样的工作代码:

public class ConditionBuilder
{
    private readonly Expression<Func<bool>> _filter;
    public ConditionBuilder(Expression<Func<bool>> filter) {
        _filter = filter;
    }
    public static ConditionBuilder And(ConditionBuilder left, ConditionBuilder right) {
        return new ConditionBuilder(Expression.Lambda<Func<bool>>(Expression.AndAlso(left._filter.Body, right._filter.Body)));
    }
    public static ConditionBuilder Or(ConditionBuilder left, ConditionBuilder right) {
        return new ConditionBuilder(Expression.Lambda<Func<bool>>(Expression.OrElse(left._filter.Body, right._filter.Body)));
    }
}

编辑2 澄清问题

表达式被转换成另一种格式。一个例子是() => ConditionBuilder.IntField(123) == 5被转换成@123 EQ 5(真正的格式是另一回事,但你明白的意思)

问题是另一种格式没有位操作符的布林重载。这意味着() => true & false被转换为True BITAND False,这不是一个有效的表达式,因为它返回int而不是布尔值。

如果我重载&to mean AndAlso

exp1 & exp2

是一个有效的表达式,但是

() => x != 3 & y != 5

我的第二个问题是,在c#中隐式转换为bool是否会像在c++中那样导致问题

表达式与运算符&&的组合

我将重载&操作符。这并不令人困惑,因为&可以是逻辑的,也可以是位的,这取决于上下文。

要重载&操作符,必须为重载的操作符使用包装器类(例如ConditionBuilder)。

您可以在文档中看到一个完整的示例,重载各种操作符,包括true操作符http://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx中的&


使用ConditionBuilder演示重载&操作符的简单示例。

void Main ()
{
    int x = 1;
    int y = 1;
    var exp1 = new ConditionBuilder (() => x != 3);
    var exp2 = new ConditionBuilder (() => y != 5);
    var exp3 = exp1 & exp2;
    Console.WriteLine (exp3.Execute ());
}
public class ConditionBuilder
{
    private readonly Expression<Func<bool>> _filter;
    public ConditionBuilder(Expression<Func<bool>> filter) {
        _filter = filter;
    }
    public bool Execute() {
        return _filter.Compile()();
    }
    public static ConditionBuilder And(ConditionBuilder left, ConditionBuilder right) {
        return new ConditionBuilder(Expression.Lambda<Func<bool>>(Expression.AndAlso(left._filter.Body, right._filter.Body)));
    }
    public static ConditionBuilder Or(ConditionBuilder left, ConditionBuilder right) {
        return new ConditionBuilder(Expression.Lambda<Func<bool>>(Expression.OrElse(left._filter.Body, right._filter.Body)));
    }
    public static ConditionBuilder operator & (ConditionBuilder left, ConditionBuilder right) {
        // Note this could confuse users for the problem discussed below.
        // Consider using Expression.And instead of AndAlso
        return ConditionBuilder.And(left, right);
    }
    public static ConditionBuilder operator | (ConditionBuilder left, ConditionBuilder right) {
        // Note this could confuse users for the problem discussed below.
        // Consider using Expression.Or instead of OrElse
        return ConditionBuilder.Or(left, right);
    }
}

我会小心使用短路运算符(AndAlso, OrElse)与&|作为给定Foo() & Bar(),当Foo返回false Bar将被调用,这将是大多数用户意想不到的。


将ConditionBuilder的And/Or方法更改为实例成员,并且只将右手边作为参数。

public ConditionBuilder And(ConditionBuilder right) {
    return new ConditionBuilder(Expression.Lambda<Func<bool>>(Expression.AndAlso(_filter.Body, right._filter.Body)));
}

这给出了像var exp3 = exp1.And(exp2);这样的语法,它比原来的语法更整洁,并且可以很好地链接exp1.And(exp2).And(exp3)

在混合And/Or时,您必须小心,因为exp1.Or(exp2).And(exp3)会给您(exp1 | exp2) & exp3而不是exp1 | (exp2 & exp3)。这可以使用显式括号来避免,例如(exp1.Or(exp2)).And(exp3) vs exp1.Or(exp2.And(exp3))


支持&&

要支持&&,你需要在ConditionBuilder中支持truefalse操作符,这意味着编译和执行表达式。

public static bool operator true (ConditionBuilder left) {
    return left.Execute();
}
public static bool operator false (ConditionBuilder left) {
    return !left.Execute();
}

在使用LinqPAD进行测试时,var exp3 = exp1 && exp2的结果不太理想。

  1. exp1必须立即执行以解决短路。
  2. 当exp1为true时,结果表达式等于:
    var exp3 = () => x != 3 && y != 5
  3. 当exp1为false时,结果表达式等于:
    var exp3 = () => x != 3