再次受到-5
问题的启发!
我阅读this comment的[@Quartermeister]并感到惊讶!
为什么这会编译
switch(1) {
case 2:
}
但事实并非如此。
int i;
switch(i=1) {
case 2: // Control cannot fall through from one case label ('case 2:') to another
}
这两个
switch(2) {
case 2: // Control cannot fall through from one case label ('case 2:') to another
}
更新:
-5
问题变为-3
。
答案 0 :(得分:21)
应该编译。 C#规范要求switch部分至少有一个语句。解析器应该禁止它。
让我们忽略解析器允许空语句列表的事实;这不是什么相关的。规范说开关部分的末端必须没有可达到的终点;这是相关的一点。
在上一个示例中,交换机部分有一个可到达的终点:
void M(int x) { switch(2) { case 2: ; } }
所以一定是个错误。
如果你有:
void M(int x) { switch(x) { case 2: ; } }
然后编译器不知道x是否会是2.它保守地假定它可以,并且说该部分有一个可到达的终点,因为交换机案例标签是可达的。
如果你有
void M(int x) { switch(1) { case 2: ; } }
然后,编译器可以推断端点不可访问,因为无法访问案例标签。编译器知道常量1永远不会等于常量2。
如果你有:
void M(int x) { switch(x = 1) { case 2: ; } }
或
void M(int x) { x = 1; switch(x) { case 2: ; } }
然后你知道并且我知道终点是不可达的,但编译器不知道这一点。规范中的规则是可达性仅通过分析常量表达式来确定。包含变量的任何表达式,即使您通过其他方式知道其值,也不是常量表达式。
在过去,C#编译器存在错误,而事实并非如此。你可以这样说:
void M(int x) { switch(x * 0) { case 2: ; } }
并且编译器会认为x * 0必须为0,因此无法访问案例标签。那是一个bug,我在C#3.0中修复过。规范说只有常量用于该分析,x
是变量,而不是常量。
现在,如果程序是 legal ,那么编译器可以使用这样的高级技术来影响生成的代码。如果你这样说:
void M(int x) { if (x * 0 == 0) Y(); }
然后编译器可以像编写
一样生成代码void M(int x) { Y(); }
如果它想要的话。但是,为了确定语句可达性,它不能使用x * 0 == 0
为真的事实。
最后,如果你有
void M(int x) { if (false) switch(x) { case 2: ; } }
然后我们知道交换机不可达,因此该块没有可达到的终点,所以这是令人惊讶的合法。但鉴于上面的讨论,你现在知道了
void M(int x) { if (x * 0 != 0) switch(x) { case 2: ; } }
不会将x * 0 != 0
视为false
,因此终点被视为可到达。
答案 1 :(得分:2)
在Visual Studio 2012中,第一个原因显而易见。编译器确定代码无法访问:
switch (1)
{
case 2:
}
警告:检测到无法访问的代码。
在另外两种情况下,编译器报告"控制不能从一个案例标签('案例2:')转到另一个"。我没有看到它说"(' case 1')"在任何一个失败的案件中。
我猜编译器对于持续评估并不积极。例如,以下内容是等效的:
int i;
switch(i=1)
{
case 2:
}
和
int i = 1;
switch(i)
{
case 2:
}
在这两种情况下,编译器都会尝试生成代码,当 进行评估并确定您正在编写的内容时:
switch (1)
{
case 2:
}
确定代码无法访问。
我怀疑"为什么不编译"答案将是"因为我们让JIT编译器处理积极的优化。"
答案 2 :(得分:1)
好吧,问题就在于编译器完全优化了开关,这里有证明:
static void withoutVar()
{
Console.WriteLine("Before!");
switch (1)
{
case 2:
}
Console.WriteLine("After!");
}
当用ILSpy反编译时,向我们展示了这个IL:
.method private hidebysig static
void withoutVar () cil managed
{
// Method begins at RVA 0x2053
// Code size 26 (0x1a)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Before!"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: br.s IL_000e
IL_000e: ldstr "After!"
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ret
} // end of method Program::withoutVar
在任何地方都没有回忆起switch语句。我认为它没有优化第二个的原因可能与运算符重载和排序有关。因此,我可能有自定义类型,当分配到1
时,它会变为2
。但是,我不完全确定,在我看来应该提交错误报告。