在C#中简化丑陋的嵌套if-else树的方法

时间:2009-10-24 16:25:51

标签: c#-3.0

有时我在C#3.5中编写丑陋的if-else语句;我知道一些不同的方法来简化表驱动开发,类层次结构,无限方法等等。 问题在于替代方案仍然比编写传统丑陋的if-else语句的范围更广,因为没有惯例。

嵌套if-else的深度对C#​​3.5来说是否正常?您期望看到哪些方法而不是嵌套if-else第一个?第二个?

如果我有10个输入参数,每个参数有3个状态,我应该将函数映射到每个参数的每个状态的组合(实际上更少,因为并非所有状态都有效,但有时仍然很多)。我可以将这些状态表示为哈希表键和处理程序(lambda),如果键匹配则将调用它们。

它仍然是表驱动,数据驱动的开发组合。想法和模式匹配。

我正在寻找的是扩展C#这样的脚本编写方法(C#3.5就像编写脚本) http://blogs.msdn.com/ericlippert/archive/2004/02/24/79292.aspx

11 个答案:

答案 0 :(得分:8)

好问题。 “条件复杂性”是一种代码气味。 Polymorphism是你的朋友。

  

条件逻辑在其初期是无辜的,当它易于理解并包含在其中时   几行代码。不幸的是,它很少老化。您实现了几个新功能   突然你的条件逻辑变得复杂和膨胀。 [Joshua Kerevsky:重构模式]

如果要学习使用Guard Clauses,你可以做的最简单的事情就是避免嵌套if。

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};  

我发现的另一件事情很简单,它使你的代码自我记录,是Consolidating conditionals

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

与条件表达式相关的其他有价值的refactoring技术包括Decompose ConditionalReplace Conditional with VisitorSpecification PatternReverse Conditional

答案 1 :(得分:4)

简单。拿出if的主体并从中取出一个方法。

这是有效的,因为大多数if语句的格式为:

if (condition):
   action()

在其他情况下,更具体地说:

if (condition1):
   if (condition2):
      action()

简化为:

if (condition1 && condition2):
   action()

答案 2 :(得分:4)

有很多旧的“形式主义”试图封装极其复杂的表达式来评估许多可能的独立变量,例如“决策表”:

http://en.wikipedia.org/wiki/Decision_table

但是,如果可能的话,我将加入合唱团,提出明智地使用三元运算符的想法,找出最不可能的条件,如果遇到这些条件,可以通过先排除它们来终止其余的评估,并添加...反过来...试图分解最可能的条件和状态,可以让你继续进行而不测试“边缘”案例。

Miriam(上图)的建议令人着迷,甚至优雅,是“概念艺术”;我实际上是要尝试一下,试图“支持”我的怀疑,它会导致代码更难维护。

我务实的一面说,在没有一个非常具体的代码示例,以及条件及其相互作用的完整描述的情况下,这里没有“一刀切”的答案。

我是“标志设置”的粉丝:这意味着无论何时我的应用程序进入一些不太常见的“模式”或“状态”,我都会设置一个布尔标志(对于类来说甚至可能是静态的):对我而言,这简化了稍后写下复杂的if / then else评估。

最好,比尔

答案 3 :(得分:2)

我是三元运营商的忠实粉丝,被许多人所忽视。根据条件为变量赋值是很好的。像这样

foobarString = (foo == bar) ? "foo equals bar" : "foo does not equal bar";

请尝试this article了解详情。

它不会解决你所有的问题,但它非常经济。

答案 4 :(得分:2)

我知道这不是您正在寻找的答案,但如果没有上下文,您的问题很难回答。问题在于重构这样的事情的方式实际上取决于你的代码,它正在做什么,以及你想要完成什么。如果你说你正在检查这些条件中对象的类型,我们可能会抛出像'use polymorphism'这样的答案,但有时候你实际上只需要一些if语句,有时甚至需要那些语句可以重构成更简单的东西。如果没有代码示例,很难说您所属的类别。

答案 5 :(得分:2)

几年前,一位教练告诉我,3是一个神奇的数字。当他应用它 - 其他陈述时,他建议如果我需要更多的3,那么我应该使用案例陈述。

   switch (testValue)
   {
      case = 1:
         // do something
         break;
      case = 2:
         // do something else
         break;
      case = 3:
         // do something more
         break;
      case = 4
         // do what?
         break;
      default:
         throw new Exception("I didn't do anything");
   }

如果您的语句超过3深度,那么您应该将其视为有更好方法的标志。可能像Avirdlg建议的那样,将嵌套的if语句分成1个或多个方法。如果你觉得你完全坚持使用多个if-else语句,那么我会将所有if-else语句包装到一个单独的方法中,这样就不会让其他代码变得丑陋。

答案 6 :(得分:1)

如果整个目的是根据各种条件的状态为某个变量赋值,我使用了一个ternery算子。

如果If Else子句正在执行单独的功能块。并且条件很复杂,通过创建临时布尔变量来保持复杂布尔表达式的真/假值来简化。这些变量应适当命名,以表示复杂表达式计算的商业意义。然后使用If else synatx中的布尔变量而不是复杂的布尔表达式。

答案 7 :(得分:1)

我发现自己有时做的一件事是将return后面的条件颠倒过来;连续几个这样的测试可以帮助减少ifelse的嵌套。

答案 8 :(得分:1)

不是C#答案,但您可能希望模式匹配。通过模式匹配,您可以获取多个输入,并对所有输入执行同步匹配。例如(F#):

let x=
  match cond1, cond2, name with
  | _, _, "Bob"     -> 9000 // Bob gets 9000, regardless of cond1 or 2
  | false, false, _ -> 0 
  | true, false, _  -> 1
  | false, true, _  -> 2
  | true, true, ""  -> 0 // Both conds but no name gets 0
  | true, true, _   -> 3 // Cond1&2 give 3

您可以表达任何组合来创建匹配(这只是表面划痕)。但是,C#不支持这一点,我怀疑它会很快。同时,有一些尝试在C#中尝试此操作,例如:http://codebetter.com/blogs/matthew.podwysocki/archive/2008/09/16/functional-c-pattern-matching.aspx。谷歌可以出现更多;也许一个人会适合你。

答案 9 :(得分:0)

尝试使用策略或命令等模式

答案 10 :(得分:0)

在简单的情况下,你应该能够解决基本的功能分解问题。对于更复杂的场景,我使用Specification Pattern取得了巨大的成功。