结合表达式与运算符&&

时间:2011-11-23 14:52:53

标签: c# lambda

我的类有点像Linq To Sql Where子句。

它从表达式树构建一系列操作。

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

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

该类适用于normal表达式,如上例所示,但现在我需要组合表达式的功能。

我添加了And,或者像

这样的方法
var exp1 = () => x != 3;
var exp2 = () => y != 5;
var exp = ConditionBuilder.And(exp1, exp2);

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

我想写

var exp = exp1 && exp2;

但是因为我无法直接重载操作员&amp;&amp;我需要找到一些其他解决方案。 棘手的部分是结果操作没有按位运算符的布尔重载。即exp1&amp;的结果exp2是int而不是bool。 (我可以通过添加!= 0

解决这个问题

所以现在我的问题是:

  • 如果我让操作员&amp;是一个逻辑表达式(即AndAlso)?
  • 运营商&amp;&amp;如果我超负荷将会工作/ true / false但这也会创建一个隐式的布尔转换。我知道隐式布尔转换是你想在C ++中避免的,但我不确定它对C#来说有什么影响。此外,被覆盖的真假是否应该表达? (即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(真正的格式是别的,但你明白了)

问题是其他格式对于按位运算符没有booleen重载。这意味着() => true & false被转换为True BITAND False,这不是一个有效的表达式,因为它返回一个int而不是一个布尔值。

如果我超载&amp;意思是AndAlso

exp1 & exp2

是一个有效的表达式,但

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

不是。

我的第二个问题是,如果隐式转换为bool会导致C#出现问题,就像在C ++中一样。

1 个答案:

答案 0 :(得分:3)

我会重载&运算符。这并不令人困惑,因为&可以是逻辑或按位的,具体取决于上下文。

要重载&运算符,您必须为重载运算符使用包装类(例如ConditionBuilder)。

您可以在真实运算符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);
    }
}

当Foo返回false时,我会小心使用短路运算符(AndAlsoOrElse)与&|给定Foo() & 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)

混音和/或你必须小心,因为exp1.Or(exp2).And(exp3)会给你(exp1 | exp2) & exp3而不是exp1 | (exp2 & exp3)。使用明确的括号可以避免这种情况,例如(exp1.Or(exp2)).And(exp3) vs exp1.Or(exp2.And(exp3))


支持&amp;&amp;

的问题

要支持&&,您需要支持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为真时,结果表达式相当于:
    var exp3 = () => x != 3 && y != 5
  3. 当exp1为false时,结果表达式相当于:
    var exp3 = () => x != 3