Answers:
开关盒几乎总是应该有一个default
盒。
使用理由 default
1.“捕捉”意外值
switch(type)
{
case 1:
//something
case 2:
//something else
default:
// unknown type! based on the language,
// there should probably be some error-handling
// here, maybe an exception
}
2.处理“默认”操作,这种情况属于特殊行为。
您会在菜单驱动程序和bash shell脚本中看到很多。当变量在switch-case之外声明但未初始化时,您可能还会看到此情况,并且每种情况都会将其初始化为不同的东西。在这里,默认值也需要对其进行初始化,以便访问该变量的代码行不会引发错误。
3.让某人阅读您的代码,您已经解决了这种情况。
variable = (variable == "value") ? 1 : 2;
switch(variable)
{
case 1:
// something
case 2:
// something else
default:
// will NOT execute because of the line preceding the switch.
}
这是一个过于简化的示例,但重点是,阅读代码的人不要奇怪为什么variable
不能是1或2以外的东西。
我能想到的唯一不使用的情况default
是,当开关正在检查某项东西时,可以很容易地忽略其他所有明显的选择
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
// no default really required here
}
enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;
,生成的有效MyEnum
实例不等于FOO
或BAR
。如果这些问题中的任何一个都将导致项目错误,则默认情况下将帮助您非常快速地发现错误,通常无需调试器!
没有。
如果没有默认动作,上下文很重要。如果您只想按照一些价值观行事怎么办?
以阅读游戏按键为例
switch(a)
{
case 'w':
// Move Up
break;
case 's':
// Move Down
break;
case 'a':
// Move Left
break;
case 'd':
// Move Right
break;
}
新增:
default: // Do nothing
只是浪费时间,无缘无故地增加了代码的复杂性。
// do nothing
注释的default子句可以清楚地表明,如果未涵盖所有情况,则是“确定”的,与之相比,其他switch语句则是“确定的”。
在某些情况下,不使用默认大小写可能实际上是有益的。
如果您的切换用例是枚举值,则通过不使用默认用例,如果缺少任何用例,则可以得到编译器警告。这样,如果将来添加新的枚举值,而您又忘记在开关中添加这些值的大小写,则可以在编译时查找问题。您仍应确保代码对未处理的值采取适当的措施,以防将无效值强制转换为枚举类型。因此,这对于最简单的情况可能是最好的,在这种情况下,您可以在枚举情况下返回而不是中断。
enum SomeEnum
{
ENUM_1,
ENUM_2,
// More ENUM values may be added in future
};
int foo(SomeEnum value)
{
switch (value)
{
case ENUM_1:
return 1;
case ENUM_2:
return 2;
}
// handle invalid values here
return 0;
}
无论您使用哪种语言,我都将始终使用默认子句。
事情可以而且确实会出错。价值将不是您所期望的,依此类推。
不想包含默认子句意味着您有信心知道一组可能的值。如果您认为自己知道一组可能的值,那么,如果该值超出了该组可能的值,则希望得到通知-这肯定是错误的。
这就是为什么您应该始终使用默认子句并引发错误的原因,例如在Java中:
switch (myVar) {
case 1: ......; break;
case 2: ......; break;
default: throw new RuntimeException("unreachable");
}
除了“不可达”字符串外,没有其他理由需要包含更多信息。如果确实发生,则无论如何都将需要查看源和变量的值等,并且异常stacktrace将包含该行号,因此无需浪费时间在异常消息中编写更多文本。
throw new RuntimeException("myVar invalid " + myVar);
因为这样可以为您提供足够的信息,以找出并修复错误,而不必使用调试器和检查变量,如果这是很少发生且难以重现的问题,则可能会很困难。
default:
。但是更常见的是,人们会忽略默认值,因为有人认为myVar
除了列出的值之外,它永远不会有其他值。但是,当变量的值不同于其“可能”具有的值时,我无法计数在现实生活中感到惊讶的次数。在那种情况下,我一直很感谢能够让我马上看到异常的异常,而不是以后引起其他错误(更难调试)或错误答案(在测试中可能被忽略)。
AssertionError
而不是RuntimeException
,因为这种情况与声明myVar
为已处理值之一的断言非常相似。而且,有人抓住并吞下错误的机会也较小。
在我的公司中,我们为航空电子和国防市场编写软件,并且始终包含默认语句,因为必须明确处理switch语句中的所有情况(即使只是说“不做任何事情”的注释)。我们不能仅仅因为行为不当或简单地因意外(甚至我们认为不可能的)价值而崩溃就买得起该软件。
可以讨论的是,默认情况并非总是必要的,但是通过始终要求它,我们的代码分析器可以轻松地检查它。
“ switch”语句是否应始终包含默认子句?否。通常应包含默认值。
仅当需要执行某些操作(例如断言错误条件或提供默认行为)时,才包含default子句才有意义。包含一个“仅仅因为”是对货物的狂热编程,它没有任何价值。这就是说所有“ if”语句都应包含“ else”的“开关”。
这是一个无意义的小例子:
void PrintSign(int i)
{
switch (Math.Sign(i))
{
case 1:
Console.Write("positive ");
break;
case -1:
Console.Write("negative ");
break;
default: // useless
}
Console.Write("integer");
}
这等效于:
void PrintSign(int i)
{
int sgn = Math.Sign(i);
if (sgn == 1)
Console.Write("positive ");
else if (sgn == -1)
Console.Write("negative ");
else // also useless
{
}
Console.Write("integer");
}
default:
在这些情况下,a会编纂您的假设。例如,什么时候sgn==0
可以打印integer
(正面或负面)都可以,还是这是一个错误?对我来说,阅读这段代码很难说。我假设您不想zero
在这种情况下编写代码integer
,并且程序员做出的假设sgn
只能是-1或+1。如果真是这样,拥有default:
可以让程序员早日发现假设错误并更改代码。
拥有的时候不是它默认的条款真正需要的是防御性编程 这通常会导致代码,是因为太多的错误处理代码过于复杂。错误处理和检测代码会损害代码的可读性,使维护工作更加困难,并最终导致超出其解决范围的错误。
因此,我认为,如果不应达到默认值,则不必添加它。
请注意,“不应该达到”是指如果达到要求,这是软件中的错误-您确实需要测试由于用户输入等原因可能包含不需要的值的值。
我会说这取决于语言,但是在C语言中,如果要打开枚举类型并处理所有可能的值,则最好不包括默认大小写。这样,如果以后添加一个附加的enum标签,而忘记将其添加到开关中,那么胜任的编译器将向您发出有关丢失情况的警告。
gcc -Wall
(称职的编译器的最低公分母)给出有关switch语句中未处理的枚举的警告。
如果您知道switch语句将仅具有一组严格定义的标签或值,则只需覆盖基数即可,这样您将始终获得有效的结果。成为其他值的最佳处理程序。
switch(ResponseValue)
{
default:
case No:
return false;
case Yes;
return true;
}
default
这里是否还需要冒号?还是这样做允许某些特殊的语法让您忽略它?
至少在Java中不是必需的。根据JLS的说法,它最多可以存在一个默认情况。这意味着没有默认情况是可以接受的。有时它还取决于您使用switch语句的上下文。例如,在Java中,以下开关块不需要默认大小写
private static void switch1(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
break;
case "Tuesday":
System.out.println("Tuesday");
break;
}
}
但是在以下期望返回String的方法中,默认情况很方便,可以避免编译错误
private static String switch2(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
return name;
case "Tuesday":
System.out.println("Tuesday");
return name;
default:
return name;
}
}
尽管您可以通过仅在末尾使用return语句来避免上述方法的编译错误而无需使用默认大小写,但是提供默认大小写使其更具可读性。
有些(过时的)准则是这样说的,例如MISRA C:
最终默认子句的要求是防御性编程。该条款应采取适当的措施或包含关于为何不采取任何措施的适当注释。
该建议已过时,因为它不是基于当前的相关标准。Harlan Kassler所说的是明显的遗漏:
省略默认大小写,可使编译器在看到未处理的大小写时有选择地发出警告或失败。毕竟,静态可验证性比任何动态检查都好,因此,在需要动态检查时也不值得牺牲。
正如Harlan所演示的那样,可以在切换后重新创建默认情况下的等效功能。当每种情况都是早期归还时,这是微不足道的。
从广义上讲,动态检查的典型需求是输入处理。如果值来自程序的控制范围之外,则无法信任该值。
这也是Misra采取极端防御性编程的立场,在这种情况下,只要物理上可表示无效值,就必须检查该值,无论该程序是否正确无误。如果在出现硬件错误的情况下软件需要尽可能可靠,则这是有道理的。但是正如Ophir Yoktan所说,大多数软件最好不要“处理”错误。后者的做法有时称为进攻性编程。
这是一个可选的编码“约定”。取决于用途是是否需要。我个人认为,如果您不需要它,就不应该在那里。为什么要包含用户不会使用或无法访问的内容?
如果情况可能有限(即布尔值),则默认子句是多余的!
如果switch
语句中没有默认情况,那么如果该情况在某个时间点出现,则该行为可能是不可预测的,而这在开发阶段是无法预测的。包括default
案例是一个好习惯。
switch ( x ){
case 0 : { - - - -}
case 1 : { - - - -}
}
/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */
这样的做法可能导致错误,例如NULL取消引用,内存泄漏以及其他类型的 严重错误。
例如,我们假设每个条件都会初始化一个指针。但是,如果default
应该产生大小写,并且在这种情况下我们不进行初始化,则很可能出现空指针异常。因此,建议使用default
case语句,即使它可能很琐碎。
取决于特定语言中switch的工作方式,但是在大多数语言中,如果不区分大小写,执行将通过switch语句进行,而不会发出警告。想象一下,您期望一组值并在switch中处理它们,但是在输入中得到另一个值。什么也没发生,你什么也不知道。如果您拖欠了该案,您将知道出了点问题。
我不同意上述Vanwaril的最高票数答案。
任何代码都会增加复杂性。还必须为此进行测试和记录。因此,如果您可以使用更少的代码进行编程,那总是好的。我的意见是,我对非穷举开关语句使用默认子句,而对穷举开关语句不使用默认子句。为了确保我做对了,我使用了静态代码分析工具。因此,让我们进入细节:
非详尽的switch语句:这些应始终具有默认值。顾名思义,这些语句并不涵盖所有可能的值。这也可能是不可能的,例如,在整数值或字符串上的switch语句。在这里,我想使用Vanwaril的示例(应该指出的是,我认为他使用了该示例提出了错误的建议。在这里我用相反的方式来表达->使用默认语句):
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
default:
// cover all other values of the non-exhaustive switch statement
}
玩家可以按其他任何键。然后我们什么也做不了(可以通过在默认情况下添加注释就可以在代码中显示出来),或者它应该在屏幕上打印一些内容。这种情况可能会发生,因此很重要。
详尽的switch语句:这些switch语句涵盖所有可能的值,例如,关于等级系统类型枚举的switch语句。第一次开发代码时,很容易覆盖所有值。但是,由于我们是人类,因此很少有机会忘记一些东西。另外,如果以后添加枚举值,则必须对所有switch语句进行调整以使其再次穷举,这将打开通往错误地狱的路径。简单的解决方案是静态代码分析工具。该工具应检查所有switch语句,并检查它们是否详尽无遗或是否具有默认值。这里是详尽的switch语句的示例。首先我们需要一个枚举:
public enum GradeSystemType {System1To6, SystemAToD, System0To100}
然后我们需要一个像这样的枚举变量GradeSystemType type = ...
。详尽的switch语句将如下所示:
switch(type)
{
case GradeSystemType.System1To6:
// do something
case GradeSystemType.SystemAToD:
// do something
case GradeSystemType.System0To100:
// do something
}
因此GradeSystemType
,例如System1To3
,如果将扩展为,则静态代码分析工具应检测到没有默认子句,并且switch语句不是穷举性的,因此可以保存。
只是一件事。如果我们始终使用default
子句,则可能会出现静态代码分析工具无法检测穷举或非穷举switch语句的情况,因为它始终会检测该default
子句。这非常糟糕,因为如果将枚举值扩展另一个值并忘记将其添加到一个switch语句中,我们将不会得到通知。
我相信这是特定于语言的,对于C ++而言,枚举类类型的重要性不大。看起来比传统的C枚举更安全。但
如果您看一下std :: byte的实现,则类似:
enum class byte : unsigned char {} ;
资料来源:https : //en.cppreference.com/w/cpp/language/enum
还要考虑一下:
否则,如果T是具有固定基础类型的作用域范围或不受作用域限制的枚举类型,并且braced-init-list仅具有一个初始化器,并且从初始类型转换为基础类型的转换是非变窄的,并且初始化是直接列表初始化,然后使用将初始化程序转换为其基础类型的结果来初始化枚举。
(自C ++ 17起)
来源:https : //en.cppreference.com/w/cpp/language/list_initialization
这是枚举类的示例,代表未定义的枚举器的值。因此,您不能完全信任枚举。根据应用程序,这可能很重要。
但是,我非常喜欢@Harlan Kassler在他的帖子中所说的话,并将自己在某些情况下开始使用该策略。
只是不安全的枚举类的一个示例:
enum class Numbers : unsigned
{
One = 1u,
Two = 2u
};
int main()
{
Numbers zero{ 0u };
return 0;
}