6

我有以下代码:

package main

import (
    "fmt"
)

func main() {
    switch {
    case 1 == 1:
        fmt.Println("1 == 1")
        fallthrough
    case 2 == 1:
        fmt.Println("2 == 1")
    }
}

在 go 操场上打印两条线 -请参见此处的示例。我本来希望 fallthrough 语句包括对下一个case语句的评估,但情况似乎并非如此。

当然,我总是可以使用一堆if语句,所以这不是一个真正的障碍,但我很好奇这里的意图是什么,因为在我看来这是一个不明显的结果。

有人愿意解释吗?例如:在这段代码中,我怎样才能执行第 1 和第 3 种情况?

4

3 回答 3

5

Switch不是一堆ifs。它更类似于if {} else if {}构造,但有一些曲折 - 即breakfallthrough。不可能让 switch 执行第一种和第三种情况 - switch 不会检查每个条件,它会找到第一个匹配并执行它。就这样。

它的主要目的是遍历可能值的列表并为每个值执行不同的代码。实际上,在 C(switch 语句的来源)中,switch 表达式只能是整数类型,而 case 值只能是 switch 表达式也将被比较的常量。直到最近,语言才开始在 switch case 中添加对字符串、布尔表达式等的支持。

至于 fallthrough 逻辑,它也来自 C。C 中没有 fallthrough 运算符。在 C 中,除非遇到 break 运算符,否则执行会进入下一个 case(不检查 case 值)。这种设计的原因是有时您需要做一些特殊的事情,然后执行与另一种情况相同的步骤。所以,这个设计只是允许这样做。不幸的是,它很少有用,所以默认情况下,当程序员忘记放入一个 break 语句时,默认情况下会造成更多麻烦,然后在真正故意省略该 break 时提供帮助。因此,许多现代语言将这种逻辑更改为默认情况下永远不会失败,并且如果确实需要失败,则需要明确的失败语句。

不幸的是,很难想出一个非人为的例子来说明失败是有用的,它足够短以适应答案。正如我所说,这是相对罕见的。但有时你需要编写类似这样的代码:

if x == a || x == b {
  if x == a {
    // do action a
  }
  // do action ab
} else if x == c {
   // do action c
} else if x == d {
  // do action d
}

事实上,我最近在我的一个项目中需要类似结构的代码。所以,我改用 switch 语句。它看起来像这样:

switch x {
  case a: // do action a
          fallthrough
  case b: // do action ab
  case c: // do action c
  case d: // do action d
}

你从这个问题的切换在功能上等同于:

if 1 == 1 || 2 == 1 {
    if 1 == 1 {
        fmt.Println("1 == 1")
    }
    fmt.Println("2 == 1")
}
于 2017-07-25T12:11:48.693 回答
5

据推测,Go 的失败行为是以 C 为模型的,它总是这样工作。在 C 中,switch语句只是条件 goto 链的简写,因此您的特定示例将被编译为好像它是这样编写的:

    # Pseudocode
    if 1 == 1 goto alpha
    if 2 == 1 goto beta
alpha:
    fmt.Println("1 == 1")
beta:
    fmt.Println("2 == 1")

如您所见,一旦执行进入alpha案例,它将继续向下流动,忽略beta标签(因为标签本身并没有真正做任何事情)。条件检查已经发生并且不会再发生。

因此,fallthrough 语句的非直观性质switch仅仅是因为switch语句是隐蔽的 goto 语句。

于 2017-07-23T18:57:35.523 回答
3

语言规范

“fallthrough”语句将控制转移到表达式“switch”语句中下一个 case 子句的第一个语句。它只能用作此类子句中的最终非空语句。

这似乎完美地描述了您观察到的行为。

于 2017-07-23T18:56:17.310 回答