【问题标题】:Why do I need to use break?为什么我需要使用break?
【发布时间】:2009-10-14 19:08:31
【问题描述】:

我想知道为什么 C# 要求我在 switch 语句中使用 break,尽管根据定义不允许贯穿语义。因此,编译器可以在每个 case-block 的末尾生成 break 并为我省去麻烦。

但是,我可以提出一种情况(已在本网站上讨论过),这可能是明确使用 break 的原因:

switch (foo) {
    case 0:
    case 1:
        bar();
        break;
    default:
        break;
}

这里,如果 foo 的值为 0 或 1,则调用方法 bar()

如果编译器会自行生成break 语句,则此选项将在最真实的意义上中断。是这样吗,这是强制休息的原因还是有其他好的理由?

【问题讨论】:

  • 我个人认为这是 C# 团队做出的一个糟糕的设计决定,也是他们失败的极少数地方之一。
  • @Chris:我强烈建议您阅读已接受答案中链接的 Eric 的博客。它解决了几件我猜是让你烦恼的事情。
  • 克里斯,你是怎么做到的?

标签: c# switch-statement


【解决方案1】:

这个问题是以虚假为前提的,因此无法回答。该语言不需要在 switch 部分的末尾中断。该语言要求 switch 部分的语句列表必须有一个不可到达的端点。 “break”只是最常用的具有此属性的语句。用“return;”、“goto case 2;”、“throw new Exception();”结束一个 switch 部分是完全合法的。或“while (true) {}”(或者在某些情况下,继续,尽管这通常是个坏主意。)

我们需要一个带有不可访问端点的语句列表的原因是为了强制执行“不失败”规则。

如果您的问题可以更好地表述为“当我无法通过代表我插入一个 break 语句来生成带有不可达端点的语句列表的 switch 部分时,为什么编译器不自动修复我的错误?”,那么答案是 C# 不是一种“猜测开发人员可能的意思并糊弄过去”的语言。有些语言是——例如 JScript——但 C# 不是。

我们相信,犯错的 C# 程序员希望被告知这些错误,以便他们能够有意地、故意地纠正它们,这是一件好事。我们的客户告诉我们,他们不希望编译器猜测他们的意思并希望获得最好的结果。这就是为什么 C# 也不会像 JScript 那样猜测您打算将丢失的分号放在哪里,或者在您尝试修改只读变量时静默失败,就像 JScript 那样,等等。

【讨论】:

  • ??接受的答案是指 Eric 的博客,而他自己的答案被否决了?再次投票。
  • 好吧,SolutionYogi,不是每个人都同意我的观点。我没有理由想象每个人都会这样做;每个人都有权对为什么要设计这种语言有自己的看法。我很欣赏这个姿态,但真的,不要为此感到压力。 :)
  • 尽管如此,C# 编译器对很多事情都有默认值,并且在很多情况下会尝试推断程序员的意图(IE 方法在类上默认是私有的非虚拟方法)。我的拙见是,一个案例后的隐式中断将是一个合理的默认值,即使它不是 C 开关的行为方式(我这么说是因为我不是 C 开发人员)。隐式中断不应该被归类为猜测,它可能是一个定义明确的行为,但我想这会让现有的 C 开发人员感到困惑:)
  • 让现有的 C 开发人员感到困惑是针对一项功能的大量要点。我们的很多开发人员都是从 C 语言来找我们的,部分原因是它们的语法在表面上非常相似。
  • 然后我们有两种方法可以做几乎完全相同的事情——首先做一个非常古老和笨重的语言功能。调整“开关”并不会以任何方式推进最先进的技术。并且提供两种令人困惑的相似方式来做同样的事情会给我们带来用户的困惑和巨大的测试成本。所以这不太可能发生。
【解决方案2】:

我怀疑 C# 要求开发人员在每个案例的末尾放置一个 break 或 terminal 语句的原因是为了清楚起见。

它避免了该语言的新手假设 C# 中的 switch( ) 的行为类似于 C 或 C++ 中的 switch,其中会发生失败行为。 只有在相邻的空case的情况下才会在C#中出现fall through——这是比较明显的。

编辑:实际上,在 C# 中,失败总是非法的。然而,合法的是一个案例与两个或多个标签相关联。 Eric Lippert writes at length about this行为and how it differs from C/C++ switch statements

您可能有兴趣阅读 Eric Lipperts 博客上的 this article

【讨论】:

  • +1 链接到 Eric Lippert 的,呃,博客 ;-)。是的,可能是清晰推动了这个设计决策。
  • 它还允许 JIT 灵活地重新排列代码以进行优化,而不必担心由于失败而破坏某些内容。
  • @ScottDorman - 没有失败......所以如果不需要休息,重组不会改变任何事情。如果出现故障,并且代码重新排序可能会导致问题,那么无论如何都不需要中断。
  • Crikey,他们让 C# switch 几乎和 VB Select Case 一样清晰!但不完全是,恕我直言 case 0: case 1:Case 0, 1 相比有点罗嗦和混乱
【解决方案3】:

通过将 break 设为可选,您将面临以下错误:

switch (thing_result)
{
    case GOOD_THING:
        IssueReward();
        // UH-OH, missing break!
    case BAD_THING:
        IssuePunishment();
        break;
}

C# 语言设计者试图帮助程序员避免在它之前出现的语言中的陷阱,通常是通过强制程序员更加明确(例如,明确使用 'override' 或 'new' 关键字来实现或隐藏虚拟成员)。

【讨论】:

    【解决方案4】:

    我对此事的理解是,它是为了匹配 C++ 语法而包含在内的。

    【讨论】:

    • 嗯,但 AFAIK C++ 不需要 break 语句,因为 C++ - 与 C 一样 - 允许 case-blocks 失败。
    【解决方案5】:

    如果您不需要附加中断,则有问题

    switch (number)
    {
        case 0:
        case 1:
            DoSomething();
    }
    

    如果数字 == 0 会发生什么?这是一个不做任何事情的空案例还是会执行 DoSomething()?

    【讨论】:

    • 抱歉,这不是真的。在 DoSomething() 调用之后添加中断时,C# 代码将有效,并且对于 0 和 1 这两种情况,都会调用 DoSomething() 方法。
    • @Phong,我删除了我的评论,因为其中一部分是正确的。正确的是,break 语句只会增加冗长,并不能避免混淆。但这是不正确的,因为 0 案例确实做了一些事情,1 案例做了什么......因为它与 1 案例共享主体。
    猜你喜欢
    • 2021-12-01
    • 2011-02-12
    • 2015-01-07
    • 2019-06-09
    • 2011-07-05
    • 2016-02-01
    • 1970-01-01
    相关资源
    最近更新 更多