我的类有点像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
)
所以现在我的问题是:
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 ++中一样。
答案 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时,我会小心使用短路运算符(AndAlso
,OrElse
)与&
和|
给定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))
要支持&&
,您需要支持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
的结果不太理想。
var exp3 = () => x != 3 && y != 5
var exp3 = () => x != 3