Switch声明是我热爱switch
与if/else if
结构的个人主要原因之一。这里有一个例子:
static string NumberToWords(int number)
{
string[] numbers = new string[]
{ "", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine" };
string[] tens = new string[]
{ "", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety" };
string[] teens = new string[]
{ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen" };
string ans = "";
switch (number.ToString().Length)
{
case 3:
ans += string.Format("{0} hundred and ", numbers[number / 100]);
case 2:
int t = (number / 10) % 10;
if (t == 1)
{
ans += teens[number % 10];
break;
}
else if (t > 1)
ans += string.Format("{0}-", tens[t]);
case 1:
int o = number % 10;
ans += numbers[o];
break;
default:
throw new ArgumentException("number");
}
return ans;
}
聪明人正在努力,因为string[]
应该在函数之外声明:嗯,它们是,这只是一个例子。
编译器因以下错误而失败:
Control cannot fall through from one case label ('case 3:') to another Control cannot fall through from one case label ('case 2:') to another
为什么呢?有没有办法在没有三个if
的情况下获得这种行为?
答案 0 :(得分:608)
(复制/粘贴answer I provided elsewhere)
switch
- case
可以通过case
中没有代码(请参阅case 0
)或使用特殊goto case
(请参阅case 1
)或goto default
(请参阅case 2
)表单:
switch (/*...*/) {
case 0: // shares the exact same code as case 1
case 1:
// do something
goto case 2;
case 2:
// do something else
goto default;
default:
// do something entirely different
break;
}
答案 1 :(得分:40)
“为什么”是为了避免意外跌落,我很感激。这是C和Java中不常见的bug漏洞。
解决方法是使用goto,例如
switch (number.ToString().Length)
{
case 3:
ans += string.Format("{0} hundred and ", numbers[number / 100]);
goto case 2;
case 2:
// Etc
}
在我看来,开关/外壳的总体设计有点不幸。它离C太近 - 有一些有用的变化可以在范围等方面进行。可以说,一个更聪明的开关可以进行模式匹配等会有所帮助,但这确实从开关变为“检查一系列条件” - 此时可能会要求使用其他名称。
答案 2 :(得分:26)
转换漏洞历史上是现代软件中主要的漏洞来源之一。语言设计者决定强制要求在案例结束时跳转,除非您在没有处理的情况下直接默认为下一个案例。
switch(value)
{
case 1:// this is still legal
case 2:
}
答案 3 :(得分:20)
为了在这里添加答案,我认为值得考虑与此相关的相反问题,即。为什么C首先允许掉线?
任何编程语言当然都有两个目标:
因此,任何编程语言的创建都是如何最好地服务于这两个目标之间的平衡。一方面,变得更容易变成计算机指令(无论是机器代码,字节码如IL,还是指令在执行时被解释),那么编译或解释过程将更加高效,可靠和紧凑的输出。尽管如此,这个目标导致我们只是在汇编,IL或甚至原始操作码中编写,因为最简单的编译是根本没有编译的地方。
相反,语言越表达程序员的意图,而不是为此目的采取的手段,在编写和维护期间程序就越容易理解。
现在,switch
总是可以通过将其转换为等效的if-else
块或类似链来编译,但它被设计为允许编译成特定的公共汇编模式,其中一个值取值,计算它的偏移量(无论是通过查找由值的完美散列索引的表,还是通过对值*的实际算术)。值得注意的是,今天,C#编译有时会将switch
转换为等效的if-else
,有时会使用基于散列的跳转方法(同样使用C,C ++和其他语言具有可比语法。)
在这种情况下,有两个很好的理由允许掉期:
它无论如何都是自然发生的:如果你将一个跳转表构建成一组指令,并且其中一个早期批量指令不包含某种跳转或返回,那么执行就会很自然进入下一批。允许堕落是“刚刚发生的事情”#34;如果您将switch
- 使用C转换为使用跳转表的机器代码。
在汇编中编写的编码器已经被用于等效:在汇编时手动编写跳转表时,他们必须考虑给定的代码块是否以返回结束,跳转到表,或者只是继续下一个块。因此,让编码人员在必要时添加明确的break
是自然的"对于编码人员也是如此。
当时,这是一种合理的尝试,以平衡计算机语言的两个目标,因为它与生成的机器代码和源代码的表现力有关。
四十年后,事情并不完全相同,原因如下:
switch
被转变为if-else
的可能性,因为它被视为可能最有效的方法,或者变成跳转表的特别深奥的变体方法更高。高级和低级方法之间的映射不像以前那样强大。switch
块使用了除了多个标签之外的掉落同一块,据认为,这里的用例意味着这3%实际上远高于正常值)。因此,所研究的语言使得不同寻常的事情比普通的更容易照顾。与最后两点相关,请考虑当前版本的K& R中的以下引用:
从一个案例落到另一个案例并不健全,在修改程序时容易崩溃。除了单个计算的多个标签外,应谨慎使用漏洞,并进行评论。
作为一个好的形式,在最后一个案例(默认在这里)之后休息,即使它在逻辑上是不必要的。有一天,当最后添加另一个案例时,这一点防御性编程将拯救你。
因此,从马的口中,C中的跌落是有问题的。总是记录带有评论的漏洞,这被认为是一种良好的做法,这是一个应该记录一个人做不寻常事情的一般原则的应用,因为这将导致后来检查代码和/或者当你的代码实际上是正确的时,你的代码看起来就像是一个新手的错误。
当你考虑它时,代码如下:
switch(x)
{
case 1:
foo();
/* FALLTHRU */
case 2:
bar();
break;
}
是添加一些东西以使代码中的明确显式,它不是编译器可以检测到的(或者可以检测到它的缺失)。
因此,事实上必须明确表示C#中的堕落并不会给那些在其他C风格的语言中写得很好的人增加任何惩罚,因为他们已经在秋天已经明确了-throughs。†
最后,这里使用goto
已经成为C和其他类似语言的常态:
switch(x)
{
case 0:
case 1:
case 2:
foo();
goto below_six;
case 3:
bar();
goto below_six;
case 4:
baz();
/* FALLTHRU */
case 5:
below_six:
qux();
break;
default:
quux();
}
在这种情况下,我们希望将一个块包含在为除前一个块之外的值执行的代码中执行的代码中,然后我们必须使用goto
。 (当然,有一些方法和方法可以通过不同的条件来避免这种情况,但对于与这个问题相关的所有内容都是如此)。因此,C#建立在处理我们想要在switch
中击中多个代码块的一种情况的已经正常的方式上,并且只是将其概括为覆盖掉落。它还使两个案例更方便和自我记录,因为我们必须在C中添加新标签,但可以使用case
作为C#中的标签。在C#中,我们可以删除below_six
标签并使用goto case 5
,这更清楚我们正在做什么。 (我们还必须为break
添加default
,我只是为了使上述C代码显然不是C#代码而遗漏。
因此总结如下:
break
,因为熟悉类似语言的人可以更轻松地学习语言,并且更容易移植。goto
的方法从C中使用的不同case
标签中命中相同的块。它只是将其推广到其他一些情况。goto
语句作为标签,C#使基于case
的方法比在C中更方便,更清晰。总而言之,这是一个非常合理的设计决策
*某些形式的BASIC允许人们做GOTO (x AND 7) * 50 + 240
这样的事情虽然脆弱,因此一个特别有说服力的禁止goto
的案例,确实可以显示更高的语言等价物低级代码可以基于对值的算术进行跳转的方式,这在编译结果而不是必须手动维护的情况下更合理。 Duff设备的实现尤其适用于等效的机器代码或IL,因为每个指令块通常具有相同的长度而无需添加nop
填充物。
†Duff的设备再次出现在这里,作为一个合理的例外。事实上,即使没有明确评论这种效果,使用这种和类似的模式重复操作也可以使得使用跌倒相对清晰。
答案 4 :(得分:15)
你可以'转到案例标签' http://www.blackwasp.co.uk/CSharpGoto.aspx
goto语句是一个简单的命令,它无条件地将程序的控制权转移到另一个语句。该命令经常受到一些开发人员的批评,他们主张将其从所有高级编程语言中删除,因为它可以导致spaghetti code。当有如此多的goto语句或类似的跳转语句使代码难以阅读和维护时,会发生这种情况。但是,有些程序员指出goto语句在仔细使用时可以为某些问题提供优雅的解决方案......
答案 5 :(得分:8)
他们通过设计省略了这种行为,以避免它不会被意志使用但会导致问题。
只有在案例部分中没有语句时才能使用它,例如:
switch (whatever)
{
case 1:
case 2:
case 3: boo; break;
}
答案 6 :(得分:4)
他们改变了c#的switch语句(来自C / Java / C ++)行为。我猜测的原因是人们忘记了坠落而导致的错误。我读过的一本书说使用goto来模拟,但这听起来不是一个很好的解决方案。
答案 7 :(得分:0)
诸如休息之类的跳转声明是 在每个案件块之后需要, 包括最后一个块是否是 案例陈述或违约 声明。有一个例外,(不像 C ++ switch语句),C#没有 支持隐含的堕落 一个案例标签到另一个。唯一的那个 例外是如果案例陈述有 没有代码。
答案 8 :(得分:0)
每个案例陈述需要破解或转到语句,即使它是默认情况。
答案 9 :(得分:0)
你可以通过goto关键字来实现像c ++一样。
EX:
switch(num)
{
case 1:
goto case 3;
case 2:
goto case 3;
case 3:
//do something
break;
case 4:
//do something else
break;
case default:
break;
}
答案 10 :(得分:0)
简单地说,添加Xamarin的编译器实际上是错误的,它允许通过。它应该是固定的,但尚未发布。在一些实际上已经失败的代码中发现了这一点,并且编译器没有抱怨。
答案 11 :(得分:0)
答案 12 :(得分:-1)
C#不支持使用switch / case语句。不知道为什么,但实际上没有它的支持。 Linkage
答案 13 :(得分:-12)
你忘记添加“休息”;声明到案例3.在案例2中,您将其写入if块。 因此,试试这个:
case 3:
{
ans += string.Format("{0} hundred and ", numbers[number / 100]);
break;
}
case 2:
{
int t = (number / 10) % 10;
if (t == 1)
{
ans += teens[number % 10];
}
else if (t > 1)
{
ans += string.Format("{0}-", tens[t]);
}
break;
}
case 1:
{
int o = number % 10;
ans += numbers[o];
break;
}
default:
{
throw new ArgumentException("number");
}