2009-12-22 59 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