这个开关语句中的代码味道?

时间:2009-11-25 04:45:15

标签: c++ switch-statement

我想知道应该将此样式的switch语句更改为if else语句。

switch (foo) // foo is an enumerated type
{
    case barOne:
        if (blahOne)
        {
            DoFunction(//parameters specific to barOne);
            break;
        }
   case barTwo:
        if (blahTwo)
        {
            DoFunction(//parameters specific to barTwo);
            break;
        }
   //etc.
   default:
       // Whatever happens if none of the case's conditionals are met
}

除非其中一个案件符合条件,否则基本上会发生堕落。这些情况非常相似,只是需要检查的内容和需要传递的内容不同,这就是我使用switch语句的原因。

使用if else if会更好吗?否则,它是否足够清晰,但不足以保证对该漏洞的评论?多态性也总是一种选择,但对我来说似乎有些过分。

11 个答案:

答案 0 :(得分:7)

在某些情况下,这似乎可能会发生奇怪的事情。如果foo == bar1,而blahOne是假的,但是blahTwo是真的怎么办?然后你会掉进并在foo == bar2情况下调用函数,即使foo不等于bar2。

这在实践中可能是意料之外的,但如果它确实发生过,那么调试可能会很困难。在这种情况下,我会投票给if if,因为流程更简单。

if (foo == barOne && blahOne)
{
    DoFunction(/*parameters specific to barOne*/);
}
else if (foo == barTwo && blahTwo)
{
    DoFunction(/*parameters specific to barTwo*/);
}
else
{
    // Handle the fallthrough case.
}

当然,如果意图是即使foo!= barTwo也可以评估blahTwo,那么切换可能是最好的方法,但在这种情况下我绝对赞成一些解释性注释。

答案 1 :(得分:2)

哇,我不确定我是否会称之为“代码味道”或只是一个简单的错误。我看着那段代码并立即想到“那不可能是正确的”。

切换案例应该始终在代码注释中清楚地记录,如果它不明确则需要说明理由。

15年前,当Java问世,我看到他们没有修复C switch声明的默认行为时,我感到很失望。但是,Go 已经修复了它,如果这是你想要的话,需要一个明确的fallthrough语句:

  

在case或default子句中,最后一个语句可能只是一个“fallthrough”语句(§Fallthrough statement),表示控制应该从该子句的末尾流向下一个子句的第一个语句。否则控制流到“switch”语句的末尾。

答案 2 :(得分:2)

你的代码肯定是张贴的,因为控制流程可能与下面的评论相矛盾;

如果foo是barOne且blahOne为false且blahTwo为true,则使用特定于barTwo的参数调用DoFunction()。但是foo是barOne而不是barTwo。

也许你真正的意思是以下,我认为没有任何挫折和气味。

switch (foo) // foo is an enumerated type
{
    case barOne:
        if (blahOne)
            DoFunction(//parameters specific to barOne);
        break;
   case barTwo:
        if (blahTwo)
            DoFunction(//parameters specific to barTwo);
        break;
   //etc.
   default:
       // Whatever happens if none of the case's conditionals are met
}

答案 3 :(得分:1)

似乎更有意义的是计算if / else或switch逻辑中的参数,将它们存储在变量中,然后用你计算的任何参数调用函数。

答案 4 :(得分:1)

我更喜欢一组if / else-if语句,但经过大量开发和增长后,switch和if / else-if都会开始腐烂。想象一下,如果foo枚举中定义的类型数量变大,会发生什么?交换机和if / else-if策略看起来都不漂亮。

在某些时候,您需要抽象DoSomething函数(想想策略模式)或将参数抽象到DoSomething函数(将foo枚举放入DoSomething的原型中或将参数设置为DoSomething作为基类的兄弟类型类)。

答案 5 :(得分:1)

如果我是你,我会这样做:

if (foo == barOne && blahOne) {
  DoFunction(/* params specific to barOne */);
} else if (foo == barTwo && blahTwo) {
  DoFunction(/* params specific to barTwo */);
} else {
  DoDefaultStuff();
}

这对我来说似乎更具可读性。你有太多额外的逻辑来保证一个简单的开关/案例。此外,如果您的if基本上等于if嵌套在&&(这就是它)......您应该考虑使用{{1}}

答案 6 :(得分:1)

我实际上非常喜欢它现在的样子;该开关充当短路,以基于枚举值跳过blahOne等的评估。我建议的唯一一件事就是在每个案例结束时发表评论,注意到这种情况,因为这种情况经常在偶然的情况下错过。可能有一个在顶部说出为什么会这样。

当然,我假设这实际上是预期的行为;如果不是,那么就像其他人所说的那样,这可能是完全错误的。

答案 7 :(得分:0)

似乎switchif/else if更具可读性,尤其是当有更多选项时。经验法则:如果必须对相同的数据进行三次以上的比较,请使用开关。

答案 8 :(得分:0)

我在这里看不到代码味道。代码段非常易读。根据 blahOne blahTwo 代表的内容以及代码的作用,您还可以通过将它们烘焙到第一个枚举中来增强可读性。因此,不是两个案例,每个案例都有一个if语句,而是四个案例。

答案 9 :(得分:0)

您的代码也很臭,因为您似乎计划扩展此switch语句。根据我的理解,运行长开关或if / else块是低效且难以阅读的。

老实说,最好将std :: map用于较小的东西,或者将无序的地图用于较大的东西。

或者你可以使用一个类(imo,甚至更好,因为它遵循OO)

void DoFunction(someType param){
    //do what you like
}

class Case{
    public:
        Case(someType param_, bool myBlah_): param(param_), myBlah(myBlah_) {}

    void eval() {
        if (myBlah)
            DoFunction(param);
    }
private:
    someType param;
    bool myBlah;
};

void DoSomething(Case foo){
    foo.eval();
}

答案 10 :(得分:-1)

如果引用了K& R,那么总是很花哨,所以请避免切换到a。启用更高级的测试和b。避免忘记默认和c。提高可读性