带返回的switch语句 - 代码正确性

hou*_*oft 82 c correctness switch-statement

假设我在C中有大约这个结构的代码:

switch (something)
{
    case 0:
      return "blah";
      break;

    case 1:
    case 4:
      return "foo";
      break;

    case 2:
    case 3:
      return "bar";
      break;

    default:
      return "foobar";
      break;
}
Run Code Online (Sandbox Code Playgroud)

现在很明显,"破发" S是没有必要的代码正常运行,但它有点样子不好的做法,如果我不把它们放在那里给我.

你怎么看?删除它们可以吗?或者你会保持他们增加"正确性"?

kgi*_*kis 122

删除break语句.它们不是必需的,也许一些编译器会发出"无法访问的代码"警告.

  • 这同样适用于其他无条件控制跳转语句,例如 `continue` 或 `goto` - 使用它们代替 `break` 是惯用的。 (2认同)

Not*_*tMe 30

我会完全采取不同的方法.不要在方法/功能的中间返回.相反,只需将返回值放在局部变量中并在结尾处发送它.

就个人而言,我发现以下内容更具可读性:

String result = "";

switch (something) {
case 0:
  result = "blah";
  break;
case 1:
  result = "foo";
  break;
}

return result;
Run Code Online (Sandbox Code Playgroud)

  • 理论上这是一个好主意,但它经常需要额外的控制块,这可能会妨碍可读性. (29认同)
  • "一次退出"理念在C语言中是有意义的,您需要确保正确清理事物.考虑到在C++中,你可以在任何时候通过异常将函数从函数中拉出来,一个退出哲学在C++中并不真正有用. (22认同)
  • 我这样做的主要原因是,在较长的函数中,在扫描代码时很容易错过退出向量.但是,当退出总是在结束时,那么更容易快速掌握正在发生的事情. (7认同)
  • 那么,代码中间的返回块只会让我想起GOTO滥用. (7认同)
  • @Chris:在更长的功能中,我完全同意 - 但这通常会说明更大的问题,所以重构它.在许多情况下,它是一个简单的函数返回开关,并且非常清楚应该发生什么,所以不要浪费脑力跟踪额外的变量. (5认同)
  • 如果您的函数太大,以至于您会错过 switch 情况下的 return 语句,那么可能是时候将该函数重构为较小的函数了。底部的赋值和返回还引入了可变变量,这需要您阅读*整个函数*才能理解它返回的内容。 (3认同)
  • 由于该功能几乎就是这样,我个人认为在交换机内部返回更具可读性. (2认同)
  • 对于简单的功能,我完全同意“一进一出”的政策,但肯定不是一般规则。在更复杂的情况下,它可能会导致涉及大量冗余检查的解决方案,这些检查可以通过简单地提前返回来避免。 (2认同)

Ama*_*9MF 8

我个人会删除退货并保持休息.我会使用switch语句为变量赋值.然后在switch语句后返回该变量.

虽然这是一个有争议的观点,但我一直认为良好的设计和封装意味着一种方式和一种方式.保证逻辑更加容易,并且您不会因为函数的圈复杂度而意外错过清理代码.

一个例外:如果在函数开头检测到错误参数(在获取任何资源之前),则提前返回是可以的.


Pau*_*l R 6

保持休息时间 - 如果/如果休息时间已经到位,您在以后编辑代码时不太可能遇到麻烦.

话虽如此,许多人(包括我)都认为从功能中间返回是不好的做法.理想情况下,函数应该有一个入口点和一个出口点.


Ste*_*hen 5

删除它们.从case语句中返回是惯用的,否则就是"无法访问的代码".


Han*_*Gay 5

我会删除它们。在我的书中,应将这样的无效代码视为错误,因为它会使您重复一遍,并问自己“我将如何执行该行?”


Jer*_*fin 5

我通常会在没有它们的情况下编写代码。在我看来,死代码往往表明草率和/或缺乏理解。

当然,我也会考虑这样的事情:

char const *rets[] = {"blah", "foo", "bar"};

return rets[something];
Run Code Online (Sandbox Code Playgroud)

编辑:即使经过编辑的帖子,这个总体想法也可以很好地工作:

char const *rets[] = { "blah", "foo", "bar", "bar", "foo"};

if ((unsigned)something < 5)
    return rets[something]
return "foobar";
Run Code Online (Sandbox Code Playgroud)

在某些时候,特别是如果输入值是稀疏的(例如,1、100、1000 和 10000),您需要一个稀疏数组。您可以将其实现为树或地图,相当好(当然,开关在这种情况下仍然有效)。

  • @houbysoft:在你得出结论之前仔细看看它会占用额外的内存。对于典型的编译器,使用“foo”两次(例如)将使用两个指向单个数据块的指针,而不是复制数据。您还可以期待更短的代码。除非您有大量重复值,否则它通常会节省总体内存。 (3认同)

Erk*_*Erk 5

如果您有“查找”类型的代码,您可以将 switch-case 子句单独封装在一个方法中。

我为了好玩而开发的“爱好”系统中有一些:

private int basePerCapitaIncomeRaw(int tl) {
    switch (tl) {
        case 0:     return 7500;
        case 1:     return 7800;
        case 2:     return 8100;
        case 3:     return 8400;
        case 4:     return 9600;
        case 5:     return 13000;
        case 6:     return 19000;
        case 7:     return 25000;
        case 8:     return 31000;
        case 9:     return 43000;
        case 10:    return 67000;
        case 11:    return 97000;
        default:    return 130000;
    }
}
Run Code Online (Sandbox Code Playgroud)

(是的。那是 GURPS 空间...)

我同意其他人的观点,即在大多数情况下,您应该避免在一个方法中出现多个返回,并且我确实认识到,将这个返回可能更好地实现为数组或其他形式。我刚刚发现 switch-case-return 与输入和输出之间具有 1-1 相关性的查找表非常容易匹配,就像上面的东西(角色扮演游戏中充满了它们,我确信它们存在于其他游戏中) “企业”也是如此):D

另一方面,如果 case 子句更复杂,或者 switch 语句之后发生了一些事情,我不建议在其中使用 return ,而是在 switch 中设置一个变量,以中断结束,然后返回最后变量的值。

(…第三?另一方面…你总是可以将开关重构为它自己的方法…我怀疑它会对性能产生影响,如果现代编译器甚至可以将其识别为可以内联的东西......)