在switch语句中使用'goto'语句?

60

我见过一份编码规范建议,其中写道:除非在switch语句的穿透中使用goto,否则不要使用它

我不理解。到底什么情况下可以使用goto来作为“例外”呢?


3
即使在 switch 语句中,我也不会使用它的“穿透”特性。 - Cheng Chen
8
阅读前三个单词然后“跳至结尾”。 - Greg Sansom
6
我不使用它的唯一原因是因为其他人在提到它时会自动感到沮丧。我很快就会对过于敏感的人感到厌烦(可能是因为我是一名工程师)。 - Zooba
5
“智能小助手”,在switch语句中已经有很多goto,但它们的名称不同:每个break就是一个goto - chiccodoro
4
@chiccodoro 这其实很简单,每个case实际上是一个label,而switch本质上就是一个goto。每个break都是一个goto,但每个continue也是一个goto - Spook
显示剩余4条评论
7个回答

118

在 C# 中,这种结构是非法的:

switch (variable) {
   case 2: 
       Console.WriteLine("variable is >= 2");
   case 1:
       Console.WriteLine("variable is >= 1");
}

在C++中,如果variable = 2,它会运行两行代码。这可能是有意为之的,但很容易忘记在第一个case标签结尾处加上break;。因此,在C#中,他们已将此设置为不合法。要模仿穿透行为,您必须明确地使用goto来表示您的意图:

switch (variable) {
   case 2: 
       Console.WriteLine("variable is >= 2");
       goto case 1;
   case 1:
       Console.WriteLine("variable is >= 1");
       break;
}

话虽如此,有一些情况下使用goto是解决问题的好方法。不要用"永远不要使用某个东西"的规则来关闭你的思维。如果它完全没有用处,那么它一开始就不会存在于语言中。不使用goto只是一个指导方针;它不是法律。


3
@Mehrdad:我很想读《永远不要关闭你的大脑》,但我觉得我来晚了。看起来它已经被删除了。如果你碰巧有其他参考链接,是否可以刷新一下链接?谢谢。 - curiousBoy
2
很不幸,即使在最初发布的5年后,它仍然是相关的。 - ThrowingDwarf
2
你应该一直使用的唯一“最佳实践”是“动动脑筋”。 太多人跟风,试图将方法、模式、框架等强加到不需要它们的事物上。仅仅因为某些东西是新的,或者因为某个受人尊敬的人有一个观点,并不意味着它适用于所有情况 :) 编辑:澄清一下 - 我并不认为人们应该忽略最佳实践、有价值的意见等。只是人们不应该盲目跟风,而是要思考为什么这个“东西”如此伟大,它是否适用于我正在做的事情,以及它带来了什么好处/坏处? - Jessica Pennell
我喜欢邪恶。谢谢。我现在要使用它! - jeffbRTC
显示剩余5条评论

26

C# 不像 C++ 那样默认允许 case 语句穿透(除非该 case 中没有代码)。你必须使用 break 来结束 case 语句。如果需要明确地进行 case 穿透(或跳到其它 case),可以使用 goto case。由于没有其它方式可实现此行为,大多数(合理的)编码标准都会允许使用此方法。

switch(variable)
{
case 1:
case 2:
    // do something for 1 and 2
    goto case 3;
case 3:
case 4:
    // do something for 1, 2, 3 and 4
    break;
}
一个具体的例子(按要求提供):
switch(typeOfPathName)
{
case "relative":
    pathName = Path.Combine(currentPath, pathName);
    goto case "absolute";

case "expand":
    pathName = Environment.ExpandEnvironmentVariables(pathName);
    goto case "absolute";

case "absolute":
    using (var file = new FileStream(pathName))
    { ... }
    break;

case "registry":
    ...
    break;
}

1
这就是该句子的意思,但问题是“为什么这是合理的?” - Greg Sansom
1
问题是“这个'exception'情况到底会是什么样子”。C#中没有其他选择,这就是它的正当性所在。 - Zooba
1
异常情况是什么样子,而不是实现方式。即在什么情况下您想这样做。 - Greg Sansom
2
“这个异常情况”指的是在switch语句中出现了“穿透”,使用单词“unless”表示对主题“goto”之前规定的“永不使用”的一般规则的例外。(如果我说得太快,请停止我。) - Zooba

8
   public enum ExitAction {
        Cancel,
        LogAndExit,
        Exit
    }

这更整洁了。

ExitAction action = ExitAction.LogAndExit;
switch (action) {
    case ExitAction.Cancel:
        break;
    case ExitAction.LogAndExit:
        Log("Exiting");
        goto case ExitAction.Exit;
    case ExitAction.Exit:
        Quit();
        break;
}

比这个更好(特别是如果你在Quit()中做更多的工作)。
ExitAction action = ExitAction.LogAndExit;
switch (action) {
    case ExitAction.Cancel:
        break;
    case ExitAction.LogAndExit:
        Log("Exiting");
        Quit();
        break;
    case ExitAction.Exit:
        Quit();
        break;
}

你不能使用枚举类型作为goto case的参数 - 它必须是一个常量。这真是遗憾,因为它会很方便,但这似乎就是它的行为。 - Zooba
10
是的,你可以这样做。枚举被视为常量。 - djeeg
4
抱歉之前测试时肯定输入有误,现在已经编译成功了,我发誓之前还没有成功过。抱歉。+1 - Zooba

7

除了使用goto case,您还可以跳转到另一个case语句中的标签:goto

    switch(i) {
    case "0":
        // do some stuff
        break;
    case "1":
        // other stuff, then "fall through" to next case clause
        goto Case2;
    case "2":
    Case2:
        break;
    }

这样,您可以跳转到另一个case子句,而不必担心表达式的值或类型。
虽然有一种明确的“fallthrough”关键字可以替换break会更好...

1
我刚刚意识到这是一年前的东西;非常抱歉!我还停留在2011年。 - Robert T. Adams
2
这可能是一种死灵术,但它是有益的死灵术。它实际上帮助了我。 :) +1 - moskalak
你可以使用:goto case "2"; - Filip Cornelissen

4

这是C#唯一允许 switch case 'fallthrough' 的方法。在C#中(与C、C++或Java不同),switch语句中的一个case块必须以 break 或其他显式跳转语句结尾。


3
通过对Mehrdad Afshari上述建议的扩展,我从不主张将某个结构简单地视为“糟糕的代码”或“糟糕的编码实践”。即使是“goto”语句在整个事情的大局中也有其用处。它们被认为是邪恶的教条并不是因为构造本身存在任何固有缺陷,而是因为它们被过度(和不良地)使用。
无论如何,Kernighan和Ritchie认为允许一个case穿透是正确的方式。坦率地说,我更倾向于相信他们的推理,而不是可能来自华盛顿雷德蒙德任何头脑的任何东西。或者是基于雷德蒙德任何头脑的智慧作为前提的任何教条。
如果你听到“永远不要使用xxx”,请在心里加上“没有原因”的字眼。仅仅教条地抛弃任何东西是荒谬的。设备之所以存在,是因为有理由制造它们。回过头来看,它们通常被称为“糟糕的”,并不是因为设备本身存在任何问题,而是因为使用它们的人并不完全理解它们。因此,设备几乎从来都不是“糟糕的”。几乎总是不好的是用户理解。即使是原子裂变和聚变也是如此。
我曾看到过可怕的丑陋代码结构,其唯一功能是避免使用“goto”语句。哪个更糟糕?"goto [label]",还是30行令人恶心的代码,其功能是避免输入“goto [label]”?
在教条之前寻求知识。三思而后行。这些都是有用的建议。

真正的重点是,这几乎适用于所有事物,而不仅仅是设备。例如枪支,它并不是桌子上的武器造成任何伤害,而是使用它的人。它也可以用于好事,比如保护被抢劫的人。这完全取决于用户。 - ThrowingDwarf

0

我知道这是一个老话题,但问题仍然存在。我们能否使用下面的代码来代替带有goto语句的丑陋版本?

var variable = 2;
switch (variable)
{
case 2:
Console.WriteLine("variable is >= 2");
goto case 1;
case 1:
Console.WriteLine("variable is >= 1");
break;


}

可能会被更整洁的代码所替代:

if (variable >= 2)
{
Console.WriteLine("variable is >= 2");
}
if (variable >= 1)
{
Console.WriteLine("variable is >= 1");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接