簡體   English   中英

將此C#代碼重構為更優雅的版本

[英]Refactor this C# code to a more elegant version

我正在嘗試將此代碼重構為更優雅的版本。 誰能幫忙。

  • 問題是在哪里將第一個評估結果作為參考,以便以后進行比較?
  • 我想盡可能避免使用if / switch
  • 我應該刪除Operator類並將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;
    }
}

試試這個(假設條件不為空)

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,這是不好的業力,給人的印象是您不完全了解該特定變量對讀者的作用)

消除if / switch的一種通用模式是將邏輯放在要操作的類中的if后面。 我對您的域名了解不多,無法判斷在這里是否有效。

要應用該模式,IEval將使用其他方法進行擴展,例如

IEval<T>.PerformOperation(T tempResult)

IEval的每個具體實現都將根據其建模的特定操作來實現PerformOperation,而不是使用Enum來指示操作的類型。

(根據您的代碼不確定tempResult是否為T類型)。

然后,而不是開關,寫

returnResult = condition.PerformOperation(tempResult);

我會使用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}
}

我唯一能想到的是:

使用if語句檢查您是否具有至少2個條件。

然后,而不是foreach,請使用帶有從第二個條件開始的計數器的常規for語句。

如果條件為零,則返回true。 取決於您的其他業務邏輯。

如果您有一個條件,則取值。

無論如何,我相信執行該操作的switch語句將是必需的...除非您更改代碼以執行某種類型的腳本,而這正是您要執行的實際操作。 我認為這更糟。

我唯一不喜歡的是您有一個名為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)

以下算法表現出短路(一旦已知條件為假,它將停止評估)。 它具有相同的基本設計,但在一開始就有效地使用了隱式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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM