将此C#代码重构为更优雅的版本

时间:2009-12-22 23:12:42

标签: c# refactoring

我正在尝试将此代码重构为更优雅的版本。谁能请帮忙。

  • 问题是在哪里签署比较的第一个评估结果?
  • 我想尽可能消除使用if / switch
  • 我应该删除操作符类并将Eval拆分为And和Or类,但我认为不会有太多不同

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;
    }
}

6 个答案:

答案 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。如果你想摆脱这个开关,你可以用

替换你的IEval接口
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;
}