switch statement - gestisce il caso predefinito quando non può essere raggiunto

12

Se sto usando un'istruzione switch per gestire i valori da un enum (che è di proprietà della mia classe) e ho un caso per ogni valore possibile - vale la pena aggiungere del codice per gestire il caso "predefinito"?

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

Non penso sia perché questo codice non può mai essere raggiunto (anche con i test unitari). Il mio collega non è d'accordo e pensa che questo ci protegga da comportamenti imprevisti causati dall'aggiunta di nuovi valori a MyEnum.

Che cosa dici, community?

    
posta s d 03.05.2012 - 19:35
fonte

5 risposte

32

Includere il caso predefinito non cambia il modo in cui funziona il codice, ma rende il tuo codice più gestibile. Facendo in modo che il codice si interrompa in modo ovvio (registra un messaggio e genera un'eccezione), includi una grande freccia rossa per lo stagista che la tua azienda assume la prossima estate per aggiungere un paio di funzioni. La freccia dice: "Ehi, tu! Sì, sto parlando con te! Se hai intenzione di aggiungere un altro valore all'enum, è meglio che tu aggiunga un caso anche qui". Questo sforzo in più potrebbe aggiungere pochi byte al programma compilato, che è qualcosa da considerare. Ma salverà anche qualcuno (forse anche il futuro) tra un'ora e un giorno di grattacapi improduttivi.

Aggiornamento: La situazione descritta sopra, ovvero la protezione dai valori aggiunti a un'enumerazione in un momento successivo, può essere rilevata dal compilatore. Clang (e gcc, penso) per impostazione predefinita emetterà un avviso se si attiva un tipo enumerato ma non si dispone di un caso che copra ogni possibile valore nell'enumerazione. Ad esempio, se rimuovi il caso default dallo switch e aggiungi un nuovo valore MyBaz all'enumerazione, riceverai un avviso che dice:

Enumeration value 'MyBaz' not handled in switch

Lasciando che il compilatore rilevi casi scoperti è che elimina in gran parte la necessità di quel caso di default irraggiungibile che ha ispirato la tua domanda in primo luogo.

    
risposta data 03.05.2012 - 19:55
fonte
5

Stavo solo parlando con un collega di questo stamattina - è davvero spiacevole, ma penso che la gestione del default sia richiesta per sicurezza, per due ragioni:

In primo luogo, come cita il tuo collega di lavoro, a prova di futuro il codice contro i nuovi valori aggiunti all'enum. Questo può o non può sembrare una possibilità, ma è sempre lì.

Ancora più importante, a seconda della lingua / compilatore, potrebbe essere possibile avere valori che non sono membri dell'enum nella variabile commutata. Ad esempio, in 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

Aggiungendo la semplice case default: e generando un'eccezione, ti sei protetto da questo strano caso in cui "nulla" accade ma "qualcosa" dovrebbe essere successo.

Sfortunatamente, praticamente ogni volta che scrivo un'istruzione switch, è perché sto controllando i casi di un enum. Mi auguro davvero che il comportamento "lancio di default" possa essere applicato dal linguaggio stesso (almeno aggiungendo una parola chiave).

    
risposta data 04.05.2012 - 00:24
fonte
3

Aggiungere un caso predefinito anche se non ti aspetti di raggiungerlo può essere una buona cosa. Faciliterà il debug molto più facilmente se il tuo codice genera un'eccezione "Questo non dovrebbe essere successo" subito piuttosto che più avanti nel programma, lanciando qualche misteriosa eccezione o restituendo risultati imprevisti senza errori.

    
risposta data 03.05.2012 - 19:48
fonte
2

Dico:

Prova ad aggiungere un altro tipo a MyEnum . Quindi cambia questa linea:

MyEnum myEnum = GetMyEnum();

a

MyEnum myEnum = SomethingElse;

Quindi esegui il tuo codice con il caso predefinito e senza il caso predefinito. Quale comportamento preferisci?

Avere il caso predefinito può anche essere utile per intercettare NULL valori e prevenire NullPointerExceptions .

    
risposta data 03.05.2012 - 19:43
fonte
-1

Se hai avuto la minima idea di come risparmiare tempo con il mach, insistendo sul caso predefinito, non avresti bisogno di porre la domanda. Non fare nulla in silenzio in una condizione di errore non è accettabile. Catturate silenziosamente eccezioni che non dovrebbero mai accadere? Lasciare "mine" per i programmatori che ti seguono, allo stesso modo inaccettabile.

Se il tuo codice non verrà mai modificato o modificato ed è privo di errori al 100%, il caso predefinito potrebbe non essere valido.

Ada (il nonno di robusti linguaggi di programmazione) non compilerà un interruttore su un enum, a meno che tutte le enumerazioni siano coperte o ci sia un gestore predefinito - questa funzione è nella mia lista dei desideri per ogni lingua. Il nostro standard di codifica Ada afferma che con gli switch sull'enumerazione, la gestione esplicita di tutti i valori, senza default, è il modo preferito per gestire questa situazione.

    
risposta data 04.05.2012 - 02:38
fonte

Leggi altre domande sui tag