没有break的switch语句

8
为什么 switch 语句中没有 break 的 case 选项会自动转到下一个 case,而不进行检查?
try {
    switch($param) {
        case "created":
            if(!($value instanceof \DateTime))
                throw new \Exception("\DateTime expected, ".gettype($value)." given for self::$param");
        case "Creator":
            if(!($value instanceof \Base\User)) {
                throw new \Exception(get_class($value)." given. \Base\User expected for self::\$Creator");                  
            }
        default:
            $this->$param = $value;
            break;
    }
} catch(Exception $e) {
    echo $e->getMessage();
}

如果参数是"created",它将在创建案例中进行检查,这是很好的。当检查成功时,我希望代码继续执行默认选项,因此没有break;。但是,当$param != "Creator" 时,它却继续到"Creator"!
我知道如何解决这个问题(只需在我的情况下添加默认代码"created"),但我不想重复使用那段代码。我的实际问题是:为什么它会在情况不是"Creator"的情况下继续使用"Creator"?
6个回答

18

Fallthrough是一种有意设计的特性,用于允许类似以下代码:

switch ($command) {
  case "exit":
  case "quit":
    quit();
    break;
  case "reset":
    stop();
  case "start":
    start();
    break;
}

它的设计使得执行从一个case向下运行。

default和其他case一样,只不过当没有其他case被触发时才会跳转到它。它绝不是一个“在运行完实际选择的case后再执行此操作”的指令。在你的例子中,你可以考虑:

  switch($param) {
    case "created":
        if(!($value instanceof \DateTime))
            throw new \Exception("\DateTime expected, ".gettype($value)." given for self::$param");
        break;
    case "Creator":
        if(!($value instanceof \Base\User)) {
            throw new \Exception(get_class($value)." given. \Base\User expected for self::\$Creator");                  
        }
        break;
}

$this->$param = $value;

准则是,如果某项操作不依赖于开关状态,则将其移出开关语句。


我想补充一点,虽然穿透有时很有用,但最好不要在两种情况都需要执行操作的情况下使用它(好的写法:case "created": case "creator": case "something_else": do_stuff(); 坏的写法:case "created": do_stuff(); case "creator": do_Second_stuff(); case "something_else": do_remaining_stuff();)。 - user253984
@dbemerlin:确实很危险,但如果情况短暂或使用支持显式穿透的语言(如C#)则可以接受。 - Victor Nicollet
最好添加一条注释以说明fallthrough是有意的。 - Hammerite

2
在 PHP 8 中,我们拥有 match,它与 switch 表达式相似,但显著更短:
  • 它不需要 break 语句
  • 它可以使用逗号将不同的分支组合成一个分支
  • 它返回一个值,因此您只需要一次赋值

一个例子:

$message = match ($statusCode) {
    200, 300 => null,
    400 => 'not found',
    500 => 'server error',
    default => 'unknown status code',
};

这是它的switch等效代码:
switch ($statusCode) {
    case 200:
    case 300:
        $message = null;
        break;
    case 400:
        $message = 'not found';
        break;
    case 500:
        $message = 'server error';
        break;
    default:
        $message = 'unknown status code';
        break;
}

参考资料:https://stitcher.io/blog/php-8-match-or-switch


1

因为这就是 C语言 的做法。


1

0
回答你的“实际问题”:为什么它在不是“Creator”案例的情况下继续执行“Creator”案例。 因为你没有使用break。没有它,它将继续执行下面的case。我唯一能想到的解决方法是将默认代码放到case中,并添加break
此外,在switch块中,最后一个case是default case,所以在default case中不需要使用break

0

我真的不明白你想要什么。

  1. 如果你想在所有情况下运行默认内容,只需将其放在 switch 语句后面。
  2. 如果你想仅在“created”和默认情况下运行默认内容,请交换“created”和“Creator”部分的位置,并在第一个后面加上 break。
  3. 如果你希望该代码仅在 Creator 或 created 匹配时运行,则摆脱 switch 语句并使用 if/else 或使用标志和随后的 if 语句。

所有工具都在那里。


1
这不是关于我想要什么,而是关于为什么 switch 语句会像这样工作。 - Rene Terstegen
非常抱歉我误解了问题。历史就是答案。就像编程中的大多数事情一样,都是别人说的。以下是他们的说法。有些历史背景(基本上C语言是它的起源),但那只是语法问题。 - caveman

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