我正在尝试将此代码重构为更优雅的版本。谁能请帮忙。
public interface IEval<T>
{
Func<T, bool> Expression { get; }
Operator Operator { get; }
string Key { get; }
}
public static bool Validate<T>(this T o, IList<IEval<T>> conditions)
{
var returnResult = true;
var counter = 0;
foreach (var condition in conditions)
{
var tempResult = condition.Expression(o);
if (counter == 0) //don't like this
{
returnResult = tempResult;
counter++;
}
else
{
switch (condition.Operator) //don't like this
{
case Operator.And:
returnResult &= tempResult;
break;
case Operator.Or:
returnResult |= tempResult;
break;
default:
throw new NotImplementedException();
}
}
}
return returnResult;
}
谢谢!
代码已更新:
public interface IEval<T>
{
Func<T, bool> Expression { get; }
bool Eval(bool against, T t);
}
public class AndEval<T> : IEval<T>
{
public Func<T, bool> Expression { get; private set; }
public AndEval(Func<T, bool> expression)
{
Expression = expression;
}
public bool Eval(bool against, T t)
{
return Expression.Invoke(t) & against;
}
}
public class OrEval<T> : IEval<T>
{
public Func<T, bool> Expression { get; private set; }
public OrEval(Func<T, bool> expression)
{
Expression = expression;
}
public bool Eval(bool against, T t)
{
return Expression.Invoke(t) | against;
}
}
public static class EvalExtensions
{
public static bool Validate<T>(this T t, IList<IEval<T>> conditions)
{
var accumulator = conditions.First().Expression(t);
foreach (var condition in conditions.Skip(1))
{
accumulator = condition.Eval(accumulator, t);
}
return accumulator;
}
}
答案 0 :(得分:3)
试试这个(假设条件不为空)
var accumulator = conditions.First().Expression(0);
foreach (var condition in conditions.Skip(1))
{
accumulator = condition.Operation.Evaluate(
condition.Expression(0), accumulator);
}
class AddOperation : Operation
{
public override int Evaluate(int a, int b)
{
return a & b;
}
}
你明白了。您可以通过在条件下定义一个方法使其更加紧凑,使其在自身上运行Expression()并将结果作为第一个参数传递给Evaluate:
condition.Evaluate(accumulator);
class Condition
{
public int Evaluate(int argument)
{
return Operation.Evaluate(Expression(0), argument);
}
}
(也是一个不相关的建议:永远不要称之为变量tempSomething,这是一个糟糕的业力,并给人的印象是你并不确切知道该特定变量对读者的作用)
答案 1 :(得分:2)
消除if / switch的一个通用模式是将逻辑置于您正在操作的类中的if后面。我对您的域名不了解,无法判断这是否可行。
要应用该模式,IEval将使用其他方法进行扩展,例如:
IEval<T>.PerformOperation(T tempResult)
然后,IEval的每个具体实现都将基于其建模的特定操作实现PerformOperation,而不是使用Enum来指示操作类型。
(根据你的代码,不确定tempResult是否属于T类型。)
然后写
而不是开关returnResult = condition.PerformOperation(tempResult);
答案 2 :(得分:2)
我会使用LINQ方法。喜欢 -
public static bool Validate<T>(this T o, IList<IEval<T>> conditions)
{
return conditions
.Skip(1)
.Aggregate(
conditions.First().Expression(o),
(a, b) => b.Operator == Operators.Or ? (a || b.Expression(o)) : (a && b.Expression(o))
);
}
或者如果你不喜欢三元运算符或需要更多可扩展和更好的方法,你可以使用Dictionary来存储和查找与运算符相关的函数。
public static bool Validate<T>(this T o, IList<IEval<T>> conditions)
{
return conditions
.Skip(1)
.Aggregate(
conditions.First().Expression(o),
(a, b) => operators[b.Operator](a, b.Expression(o))
);
}
public static Dictionary<Operator, Func<bool, bool, bool>> operators = new Dictionary<Operator, Func<bool, bool, bool>>()
{
{Operator.And, (a, b) => a && b},
{Operator.Or, (a, b) => a || b}
}
答案 3 :(得分:1)
我唯一能想到的是:
如果您的支票至少有2个条件,请使用if语句。
然后,使用带有从第二个条件开始的计数器的常规for语句而不是foreach。
如果条件为零,则返回true。取决于您的其他业务逻辑。
如果您有一个条件,则取值。
无论如何,我相信要执行的操作的switch语句是必要的......除非你改变代码来执行某种类型的脚本,这是你的实际操作。我觉得更糟糕。
答案 4 :(得分:1)
我唯一不喜欢的是你有一个名为counter的变量,它总是0或1.我会改为bool isFirst
。如果你想摆脱这个开关,你可以用
public interface IEval<T>{
Func<T, bool> Expression { get; }
Func<bool, bool, bool> Combinator { get; }
string Key { get; }
}
您的Combine方法将是
public Func<bool, bool, bool> Combinator {
get { return (b1, b2) => b1 | b2; }
}
或
public Func<bool, bool, bool> Combinator {
get { return (b1, b2) => b1 & b2; }
}
取决于所需的操作。
虽然可能只是添加一个方法bool Combine(bool value1, bool value2)
答案 5 :(得分:0)
以下算法表现出短路(一旦知道条件为假,就停止评估)。它具有相同的基本设计,但它在开始时有效地使用隐式true && ...
来使事情更清晰。
public static bool Validate<T>(this T o, IList<IEval<T>> conditions)
{
bool result = true;
Operator op = Operator.And;
var conditionIter = conditions.GetEnumerator();
while (result && conditionIter.MoveNext())
{
bool tempResult = conditionIter.Current.Expression(o);
switch (op)
{
case Operator.And:
result &= tempResult;
break;
case Operator.Or:
result |= tempResult;
break;
default:
throw new NotImplementedException();
}
op = condition.Operator;
}
return result;
}