为什么C#允许在case语句后面添加语句,但不允许在之前添加?

64

C#为什么允许this

var s = "Nice";
switch (s)
{
    case "HI":
        break;
    const string x = "Nice";
    case x:
        Console.Write("Y");
        break;
}

但不包括 this

var s = "Nice";
switch (s)
{
    const string x = "Nice";
    case x:
        Console.Write("Y");
        break;
}

8
还有其他语言允许吗? - Rajan Panneer Selvam
50
你为什么想要做其中的任何一个? - Jodrell
12
无论是否有人会编写这样的代码,这仍然是一个有趣的问题。在幕后一定发生了一些奇怪的作用域事情。 - Stealth Rabbi
7
@rtunercase语句需要在编译时确定常量,因为switch的实现是一个字典,而不是一系列的if/else if语句。它需要将它们转换为可以作为字典键的对象。同时,评估case不能有任何副作用,因为这些副作用在测试case时不会像在C++中那样生成。 - Servy
10
谢谢你提出这个问题,我会将它添加到我的开关语句特殊情况列表和微妙的Mono错误列表中。如果你对开关语句的不寻常用法感兴趣,请阅读我关于这个主题的文章:http://blogs.msdn.com/b/ericlippert/archive/2009/08/13/four-switch-oddities.aspx - Eric Lippert
显示剩余8条评论
3个回答

120

因为你的缩进有误导性,实际上第一段代码应该是:

var s = "Nice";
switch (s)
{
    case "HI":
        break;
        const string x = "Nice";
    case x:
        Console.Write("Y");
        break;
}
那就是,x 在一个 case 语句块中被声明(尽管在 break 之后),在这里它是有效的。然而,在 switch 语句块中直接声明是无效的——只有 casedefault 是有效的。

此外,const 声明在编译时被评估,因此即使在 break 语句之前,x 也已定义。

但需要注意的是,Mono C# 编译器无法编译此代码,它会抱怨“在当前范围内不存在名为 ‘x’ 的变量”,因此 Mono 似乎实现了比 .NET 编译器更多的检查。然而,我找不到 C# 标准中禁止使用 const 声明的任何规则,因此我认为 .NET 编译器是正确的,而 Mono 编译器是错误的。

21
"@rtuner const语句不是在运行时执行的,它们在编译时被替换。尝试在其上设置断点。" - Jodrell
6
为什么在 break 和下一个 case 之间允许有任何代码? - Magnus
14
为什么在return;之后并且在该作用域结束之前可以放置代码?那些代码是不会被执行的。简单的答案是因为他们没有费力去将其变成非法的,而将其保留下来不会真正带来问题。要求禁止这个功能只会带来更多的工作,而实现它的努力并不值得。 - Servy
2
@ChibuezeOpata 实现的方式是一个 case 语句后面可以跟任意数量的语句。它实际上会进行可达性检查,并生成一个警告,说明给定行是不可达的,但它并不费力地生成错误,即使它检测到了不可达代码。为什么他们要花额外的时间和精力将这个警告改为错误呢?这样做有什么好处吗?如果你能证明这一点,那就向微软提出建议吧。 - Servy
22
回应你最后一段内容:确实,Mono似乎做错了。但我们几乎不能责怪他们;这是一个奇怪的情况。我想知道Mono是否还会在switch语句中处理作用域的其他规则出错?我会去找出答案的! - Eric Lippert
显示剩余15条评论

7
由于语言规范不允许在switch语句中直接使用const(只允许使用case和default):
switch (expression)
{
   case constant-expression:
      statement
      jump-statement
   [default:
      statement
      jump-statement]
}

其中:

expression:整数或字符串类型的表达式。
statement:如果控制权转移到case或default,则执行嵌入式语句。
jump-statement:跳转语句,将控制权从case主体转移出去。
constant-expression:根据该表达式的值,控制权将转移到特定的case。

在第一个案例中,const是您的case逻辑的一部分。这个const之所以正常工作,是因为它在编译时被重新编写,而不是在运行时。


1
因为 switch 这样做。
jump_to_the_label_matchig(s)
{
   label1:
      ...
      done_quit_this;
   label2:
      ...
      done_quit_this;
   d'oh:
      ...
      done_quit_this;
}

而不是这个

now_jump_to_the_label_matchig(s)
{

   le'mme_wander_around_doing_things_that_could_have_been_done_before_me;

   label1:
      ...
      done_quit_this;
   label2:
      ...

我敢打赌,如果允许的话,你会发现有人愿意在那里完成他们所有的编程工作 :-)


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