instrução switch - manipulação de caso padrão quando não pode ser alcançado

14

Se eu estiver usando uma instrução switch para manipular valores de uma enumeração (pertencente à minha classe) e tiver um caso para cada valor possível - vale a pena adicionar código para manipular o caso "padrão"?

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() 
}

Eu não acho que é porque esse código nunca pode ser alcançado (mesmo com testes de unidade). Meu colega de trabalho discorda e acha que isso nos protege contra comportamentos inesperados causados ​​por novos valores sendo adicionados ao MyEnum.

O que você diz, comunidade?

SD
fonte
Digamos que MyEnum é um tipo não anulável.
sd
3
"Hoje" é não anulável. E amanhã, quando você não estiver mais mantendo o código. Ou então, quando "MyBiz" é adicionado à enumeração, mas não é o caso? Os comentários de Caleb sobre manutenção são muito relevantes.
1
Ensine ao seu compilador que é um erro fatal se houver uma opção que não cubra todos os casos.
E se alguém lançar um valor inválido e MyEnumdepois passar pelo seu switch?
Mawg diz que restabelece Monica
1
Que lingua? Se Java, você deve colocar um método dentro do Enum e apenas chamá-lo (polimorfismo), eliminando switchcompletamente a instrução.
user949300

Respostas:

34

A inclusão do caso padrão não altera a maneira como seu código funciona, mas torna seu código mais sustentável. Ao fazer a quebra de código de uma maneira óbvia (registre uma mensagem e lance uma exceção), você inclui uma grande seta vermelha para o estagiário que sua empresa contrata no próximo verão para adicionar alguns recursos. A seta diz: "Ei, você! Sim, estou falando com VOCÊ! Se você quiser adicionar outro valor à enum, é melhor adicionar um caso aqui também". Esse esforço extra pode adicionar alguns bytes ao programa compilado, o que é algo a considerar. Mas também salvará alguém (talvez até o futuro você) em algum momento entre uma hora e um dia de coçar a cabeça improdutivo.


Atualização: A situação descrita acima, ou seja, proteger contra valores adicionados a uma enumeração posteriormente, também pode ser detectada pelo compilador. Clang (e gcc, eu acho) por padrão emitirá um aviso se você ativar um tipo enumerado, mas não tiver um caso que cubra todos os valores possíveis na enumeração. Portanto, por exemplo, se você remover o defaultcaso do seu comutador e adicionar um novo valor MyBazà enumeração, receberá um aviso dizendo:

Enumeration value 'MyBaz' not handled in switch

Deixar o compilador detectar casos não descobertos é que elimina amplamente a necessidade daquele defaultcaso inacessível que inspirou sua pergunta em primeiro lugar.

Caleb
fonte
2
Ok, você me convenceu :) Eu só vou ter que aceitar o impacto nos meus números de cobertura de código.
Sd
@st Não há razão para você não poder testar esse código. Apenas faça uma compilação de teste que compila condicionalmente um valor extra na sua enumeração e depois escreva um teste de unidade que o use. Talvez isso não seja o ideal, mas provavelmente não é o único caso em que você precisa testar código que normalmente nunca será alcançado.
Caleb
Ou, você lança um valor não enum para seu tipo de enum e o usa.
Mawg diz que restabelece Monica
5

Eu também estava conversando com um colega sobre isso hoje de manhã - é realmente lamentável, mas acho que lidar com o padrão é necessário por segurança, por dois motivos:

Primeiro, como seu colega de trabalho menciona, ele prova o código contra novos valores sendo adicionados à enumeração. Isso pode ou não parecer uma possibilidade, mas está sempre lá.

Mais importante, dependendo do idioma / compilador, pode ser possível ter valores que não são membros da enumeração na sua variável comutada. Por exemplo, em 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

Ao adicionar o simples case default:e lançar uma exceção, você se protegeu contra esse caso estranho em que "nada" acontece, mas "algo" deveria ter acontecido.

Infelizmente, basicamente, sempre que eu escrevo mais uma instrução switch, é porque estou verificando os casos de uma enumeração. Eu realmente gostaria que o comportamento "jogar por padrão" pudesse ser imposto pelo próprio idioma (pelo menos adicionando uma palavra-chave).

Chris Phillips
fonte
3

Adicionar um caso padrão, mesmo que você nunca espere alcançá-lo, pode ser uma coisa boa. Isso tornará a depuração muito mais fácil se o seu código lançar uma exceção "Isso não deveria ter acontecido" imediatamente, ou mais tarde no programa, lançando alguma exceção misteriosa ou retornando resultados inesperados sem erros.

Ryathal
fonte
2

Eu digo:

Tente adicionar outro tipo para MyEnum. Então mude esta linha:

MyEnum myEnum = GetMyEnum();

para

MyEnum myEnum = SomethingElse;

Em seguida, execute seu código com o caso padrão e sem o caso padrão. Qual comportamento você prefere?

Ter o caso padrão também pode ser útil para capturar NULLvalores e impedir NullPointerExceptions.

FrustratedWithFormsDesigner
fonte
-1

Se você tivesse alguma idéia de como economizar tempo de máquina insistindo no caso padrão, não precisaria fazer a pergunta. Não fazer nada silenciosamente em uma condição de erro - você captura silenciosamente exceções que nunca deveriam acontecer? Deixando "minas" para programadores que o seguem, da mesma forma inaceitável.

Se o seu código nunca for alterado ou modificado e estiver 100% livre de erros, deixar de fora o caso padrão pode estar OK.

Ada (o avô de linguagens de programação robustas) não compila uma opção em uma enumeração, a menos que todas as enumerações sejam cobertas ou haja um manipulador padrão - esse recurso está na minha lista de desejos para todas as linguagens. Nosso padrão de codificação Ada afirma que, com opções ativadas, o tratamento explícito de todos os valores, sem padrão, é a maneira preferida de lidar com essa situação.

mattnz
fonte
Por que todos os -1's?
mattnz
2
Eu não fiz -1 você, mas eu acho que seria por causa de sua atitude;)
Friek