switch语句-处理无法达到的默认情况


15

如果我使用switch语句来处理枚举(属于我的班级)中的值,并且每个可能的值都有一个大小写,是否值得添加代码来处理“默认”大小写?

enum MyEnum
{
    MyFoo,
    MyBar,
    MyBat
}

MyEnum myEnum = GetMyEnum();
switch (myEnum)
{
    case MyFoo:
        DoFoo();
        break;
    case MyBar:
        DoBar();
        break;
    case MyBat:
        DoBat();
        break;
    default:
        Log("Unexpected value");
        throw new ArgumentException() 
}

我不认为这是因为永远无法到达此代码(即使使用单元测试也是如此)。我的同事不同意并认为这可以保护我们免受因将新值添加到MyEnum而导致的意外行为。

社区,你怎么说?


假设MyEnum是非空类型。
2012年

3
“今天”是不可为空的。如果您不再维护代码,明天呢?还是将“ MyBiz”添加到枚举而不是这种情况呢?Caleb关于维护的评论非常贴切。

1
如果编译器不能覆盖所有情况,请教您的编译器这是一个致命错误。

如果有人强制转换为无效值MyEnum然后将其传递给您的交换机该怎么办?
Mawg说恢复Monica 18'2

1
什么语言?如果是Java,则应在Enum中放置一个方法,然后直接调用它(多态),从而switch完全消除该语句。
user949300 '18

Answers:


35

包括默认情况并不会改变代码的工作方式,但确实会使代码更具可维护性。通过以明显的方式使代码中断(记录消息并引发异常),您将为公司明年夏天雇用的实习生增加一个大红色箭头,以添加一些功能。箭头说:“嘿,你!是的,我在跟你说话!如果你要给枚举添加另一个值,最好在这里也增加一个案例。” 这种额外的工作可能会在编译后的程序中增加一些字节,这是需要考虑的事情。但这也可以节省一个人(甚至可能是你将来的人)一个小时到一天的无用脑力劳动。


更新:编译器也可以捕获上述情况,即防止在以后添加到枚举的值。如果您打开枚举类型,但是Clang(我想是gcc)默认会发出警告,但没有一个包含枚举中所有可能值的情况。因此,例如,如果您default从开关中删除大小写MyBaz并向枚举添加新值,则会收到一条警告,内容为:

Enumeration value 'MyBaz' not handled in switch

让编译器检测未发现的情况是,它从根本上消除了对无法到达的default情况的需求,这种情况首先激发了您的问题。


2
好的,您已经说服了我:)我只需要接受代码覆盖率数字中的凹痕即可。
2012年

@st没有理由不能测试该代码。只需做一个测试构建,该构建有条件地在枚举中编译一个额外的值,然后编写一个使用它的单元测试。也许这并不理想,但是这可能并不是唯一需要测试通常永远不会到达的代码的情况。
卡勒布(Caleb)2012年

或者,您将非枚举值转换为枚举类型并使用它。
Mawg说恢复莫妮卡

5

我今天早上也只是在与同事讨论-确实很不幸,但是出于安全考虑,我认为必须处理默认值,原因有两个:

首先,正如您的同事所提到的,它可以防止代码过时,以防止将新值添加到枚举中。这似乎可能或不可能,但始终存在。

更重要的是,根据语言/编译器的不同,切换变量中的值可能不是枚举的成员。例如,在C#中:

MyEnum myEnum = (MyEnum) 3; // This could come from anywhere, maybe parsed from text?
// ... code goes on for a while

switch ( myEnum )
{
    case MyEnum.A:
        // ... handle A case
        break;
    case MyEnum.B:
        // ... handle B case
        break;
}

// ... code that expects either A or B to have happened

通过添加简单内容case default:并引发异常,您可以保护自己免受这种奇怪的情况的影响,在这种情况下,“什么也没有发生,但应该发生了”。

不幸的是,基本上我每次都写一个switch语句时,这是因为我正在检查枚举的大小写。我真的希望语言本身可以强制执行“默认情况下抛出”行为(至少通过添加关键字)。


3

即使您从未期望达到默认案例,添加默认案例也是一件好事。如果您的代码立即抛出“这本不应该发生”的异常,而不是在程序的后面抛出异常,则将使调试变得更加容易,该异常将引发一些神秘的异常或返回没有错误的意外结果。


2

我说:

尝试向添加其他类型MyEnum。然后更改此行:

MyEnum myEnum = GetMyEnum();

MyEnum myEnum = SomethingElse;

然后,使用默认大小写而不使用默认大小写来运行代码。您喜欢哪种行为?

设置默认大小写对于捕获NULL值和防止很有用NullPointerExceptions


-1

如果您对坚持默认情况有任何节省时间的想法,则无需提出问题。在错误情况下无所事事是不可接受的-您是否默默地捕获了永远不应该发生的异常?同样地,跟着你的程序员留下“地雷”,同样令人无法接受。

如果您的代码永远不会被更改或修改,并且100%没有错误,那么省略默认情况可能没问题。

Ada(健壮的编程语言的祖父)不会编译枚举的开关,除非涵盖了所有枚举或默认的处理程序-我希望每种语言都可以使用此功能。我们的Ada编码标准规定,打开枚举时,显式处理所有值(无默认值)是处理这种情况的首选方法。


为什么所有的-1都是?
mattnz

2
我没有-1你,但我想那是因为你的态度;)
Friek 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.