2009-12-22 94 views
1

我試圖將此代碼重構爲更優雅的版本。任何人都可以請幫忙。將此C#代碼重構爲更加優雅的版本

  • 問題出現在哪裏,作爲以後比較的第一個評估結果的標誌?
  • 我要避免使用,如果/開關,如果可能的
  • 我應該刪除操作員級和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; 
    } 
} 
+0

IEval是一個標準的.NET接口嗎?我找不到任何地方的參考。 – 2009-12-22 23:17:11

+0

沒有。我更新了代碼 – Jeff 2009-12-22 23:22:44

+0

在許多層面上這似乎都是錯誤的,尤其是您的AND和OR運算符具有相同的優先級。這可能不是編寫代碼期望的人。你不應該使用樹而不是列表嗎? – 2009-12-22 23:31:07

回答

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

你的想法。你可以把它更加緊湊通過定義條件的方法,使得它自身運行的表達式(),並將結果傳遞的第一個參數來評估:

condition.Evaluate(accumulator); 

class Condition 
{ 
    public int Evaluate(int argument) 
    { 
     return Operation.Evaluate(Expression(0), argument); 
    } 
} 

(也是一個不相關的建議:永遠不會調用可變tempSomething,這是惡業和給人的印象是,你不知道到底讀者的特定變量的角色)

+0

我認爲你的解決方案迄今爲止是最好的。謝謝。也感謝adivce – Jeff 2009-12-22 23:36:59

1

我能想到的唯一的事情就是:

if語句,它有一個檢查你有至少2個條件。

然後,而不是一個foreach,使用一個常規的語句與計數器開始在第二個條件。

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

如果您有一個條件,那麼取值。無論如何,我相信操作執行的switch語句將是必要的...除非您更改代碼以執行某種類型的腳本,這是您實際執行的操作。我認爲這更糟糕。

2

消除if/switch的一個通用模式是將if後面的邏輯放置在正在操作的類中。我對您的域名不太瞭解,無法判斷這是否適用於此。

要應用該模式,IEval將用另一種方法擴展,例如,

IEval<T>.PerformOperation(T tempResult) 

根據具體的操作,它符合IEval的每一個具體的實施將然後實現PerformOperation,而不是使用一個枚舉,指示操作的類型。

(不確定tempResult是否基於您的代碼是T類型)。

然後,而不是開關,寫

returnResult = condition.PerformOperation(tempResult); 
+0

,但你仍然有同樣的問題,你仍然需要做和/或評估 – Jeff 2009-12-22 23:27:24

+0

不,你沒有。我的答案與DrJokepu的基本相同,但他給出了更完整的答案。看看他的帖子來澄清。 – 2009-12-22 23:33:49

1

我不喜歡的唯一的事情是,你有一個變量稱爲櫃檯,將永遠是0或1。我想使它成爲一個bool isFirst代替。如果你想擺脫的開關,你可以用

public interface IEval<T>{ 
    Func<T, bool> Expression { get; } 
    Func<bool, bool, bool> Combinator { get; } 
    string Key { get; } 
} 

取代你IEval接口你的組合則方法將是要麼根據所需的

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)

+0

你是否介意如何擺脫開關?謝謝 – Jeff 2009-12-22 23:26:12

+0

對不起,我在參數列表中忘了一個bool。補充說明。 – erikkallen 2009-12-22 23:39:52

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

如果後續條件是OR條件,那麼它會將結果從false更改爲true。只是因爲結果暫時錯誤,您無法自信地停止循環。 – 2009-12-22 23:47:48

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

或者,如果你不喜歡三元操作或需要更多的可擴展性和更好的方法,你可以使用字典存儲和與運營商相關的查詢功能。

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} 
} 
+1

+1:這正是我想出的答案:) – Juliet 2009-12-23 00:10:34

+0

感謝您的精彩解決方案,但我想我更喜歡將封裝和/或邏輯分成不同的類,如DrJokepu的答案中所示。但是你的同樣偉大。 – Jeff 2009-12-23 00:22:00