表达式与运算符&&的组合
本文关键字:组合 运算符 表达式 | 更新日期: 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中支持true
和false
操作符,这意味着编译和执行表达式。
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
的结果不太理想。
- exp1必须立即执行以解决短路。
- 当exp1为true时,结果表达式等于:
var exp3 = () => x != 3 && y != 5
- 当exp1为false时,结果表达式等于:
var exp3 = () => x != 3