我有一段代码使用嵌套循环来创建一个linq表达式,它比较对象Paint中bool组合的所有可能性。它工作得很好,但是如果我想继续添加Properties to Paint,我将不得不继续为进程添加for循环。我想将其转换为使用递归,但我遇到了麻烦。任何人都可以为此提供一些见解/起点吗?
ParameterExpression param = Expression.Parameter(typeof(Paint), "t");
Expression exp = null;
object f = false;
object t = true;
List<Expression> expList = new List<Expression>();
//Properties to compare
MemberExpression memberA = Expression.Property(param, "BoolA");
MemberExpression memberB = Expression.Property(param, "BoolB");
MemberExpression memberC = Expression.Property(param, "BoolC");
MemberExpression memberD = Expression.Property(param, "BoolD");
//Loop 3 times to create expression using BoolA == true, BoolA == false, do not use BoolA
for(int aa = 0; aa <= 2; aa++)
{
Expression aExp = null;
if (aa == 0)
{
aExp = Expression.Equal(memberA, Expression.Constant(f));
expList.Add(aExp);
}
if(aa == 1)
{
aExp = Expression.Equal(memberA, Expression.Constant(t));
expList.Add(aExp);
}
for (int bb = 0; bb <= 2; bb++)
{
Expression bExp = null;
if (bb == 0)
{
bExp = Expression.Equal(memberB, Expression.Constant(f));
expList.Add(bExp);
}
if (bb == 1)
{
bExp = Expression.Equal(memberB, Expression.Constant(t));
expList.Add(bExp);
}
for(int cc = 0; cc <= 2; cc++)
{
Expression cExp = null;
if (cc == 0)
{
cExp = Expression.Equal(memberC, Expression.Constant(f));
expList.Add(cExp);
}
if(cc == 1)
{
cExp = Expression.Equal(memberC, Expression.Constant(t));
expList.Add(cExp);
}
for (int dd = 0; dd <= 2; dd++)
{
Expression dExp = null;
if (dd == 0)
{
dExp = Expression.Equal(membeDr, Expression.Constant(f));
expList.Add(dExp);
}
if (dd == 1)
{
dExp = Expression.Equal(memberD, Expression.Constant(t));
expList.Add(dExp);
}
//Process expList
//remove expression to prepare to add its opposite in its place
expList.Remove(dExp);
}
expList.Remove(cExp);
}
expList.Remove(bExp);
}
expList.Remove(aExp);
}
答案 0 :(得分:1)
您不需要递归来实现它,也不需要嵌套循环。我最初误解了这个问题,并认为你只是做true
/ false
,即每个属性有两个州。如果是这种情况,你可以利用二进制算术的便利性让常规整数计数器处理状态的枚举。
然而,即使没有这种便利,也不算太糟糕。它仍然是相同的基本技术,但是当属性数量高于63时,你最终会做一些更类似于我提到的抽象的东西。
首先,我们需要抽象的反阶级。这是一个使用字节计数器的版本,这在每个属性状态方面是过度的(支持256个状态而不仅仅是你需要的3个状态),但是使得实现比将多个计数器打包成一个字节或其他更容易类型:
class MultiCounter
{
private int _counterMax;
private byte[] _counters;
public MultiCounter(int counterCount, int counterMax)
{
_counterMax = counterMax;
_counters = new byte[counterCount];
}
public bool Increment()
{
for (int i = 0; i < _counters.Length; i++)
{
if (++_counters[i] < _counterMax)
{
return true;
}
_counters[i] = 0;
}
return false;
}
public int this[int index] { get { return _counters[index]; } }
}
以上维护计数器集,好像每个计数器都是counterMax
个数字的基数counterCount
中的数字。 Increment()
方法会增加此基数 - counterMax
数字,返回true
直到它溢出,这样调用者就可以知道所有可能的组合何时完成。
索引器提供了一种方便的方式来读取每个数字。
现在,我们可以使用这个帮助器类来实现实际的Expression
枚举:
MemberExpression[] memberExpressions =
{
Expression.Property(param, "BoolA"),
Expression.Property(param, "BoolB"),
Expression.Property(param, "BoolC"),
Expression.Property(param, "BoolD"),
};
MultiCounter multiCounter = new MultiCounter(memberExpressions.Length, 3);
List<Expression> expList = new List<Expression>(memberExpressions.Length);
do
{
expList.Clear();
for (int index = 0; index < memberExpressions.Length; index++)
{
int propertyCounter = multiCounter[index];
if (propertyCounter == 2)
{
continue;
}
expList.Add(Expression.Equal(
memberExpressions[index],
Expression.Constant(propertyCounter == 1)));
}
// Process expList here
} while (multiCounter.Increment());
(对于拼写错误或其他错误事先道歉......上面的内容仍然只是输入我的浏览器中以供说明之用)。
换句话说,对于N个布尔属性,您有3 ^ N种可能的组合,包括完全忽略该属性的选项。因此,只需使用MultiCounter
类重复从0到3 ^ N - 1的计数,即可维护此基数为3的数字。
虽然这不像你只有两个状态那么方便,但有一个好处是完成了额外的工作,现在你对你可以管理的属性数没有明显的限制。要在MultiCounter
实现中达到数组的2GB限制,即拥有2亿多个属性,您必须先以某种方式解决其他一些基本限制。 :)