2010-09-02 6 views
13

Sono solo curioso di sapere perchè questo codice ...Switch + Enum = Non tutti i percorsi di codice restituiscono un valore

enum Tile { Empty, White, Black }; 
    private string TileToString(Tile t) 
    { 
     switch (t) 
     { 
      case Tile.Empty: 
       return "."; 
      case Tile.White: 
       return "W"; 
      case Tile.Black: 
       return "B"; 
     } 
    } 

Lanci quell'errore. Non è possibile per t assumere qualsiasi altro valore, vero? Il compilatore non dovrebbe essere abbastanza intelligente da capirlo?

risposta

29

No, è possibile utilizzare qualsiasi valore int convertito in Tile. Prova questo:

Tile t = (Tile) 5; 
string s = TileToString(t); 

un enum è un insieme di nomi per i numeri, in modo efficace ... ma né il compilatore né il CLR impone che un valore del tipo enum ha un nome. È un dolore, ma è così ...

Vorrei suggerire un caso predefinito che ha generato ArgumentException (o eventualmente ArgumentOutOfRangeException).

+4

Poiché il metodo è privata e quindi il chiamante deve essere di passaggio solo roba buona (perché l'autore del chiamante è * voi *, non i tuoi utenti) Suggerisco un Debug.Fail ("Bad caller! No biscuit!") o simili nel caso predefinito. –

+5

@Eric: Forse ...tranne per il fatto che devi ancora restituire qualcosa o lanciare anche un'eccezione * per soddisfare i requisiti di raggiungibilità. Personalmente non sono mai stato un grande fan di 'Debug. *' - Tendo a piacere vedere le stesse eccezioni in modalità di rilascio come modalità di debug, ma potrebbe essere solo io. –

0
switch (t) 
{ 
    case Tile.Empty: 
     return "."; 
    case Tile.White: 
     return "W"; 
    case Tile.Black: 
     return "B"; 
    default: throw new NotSupportedException(); 
} 

Come Jon ha sottolineato, il valore è integrale - un enum può essere lanciato da qualsiasi valore integrale. Hai solo bisogno di gestire il valore predefinito.

1

Ciò è dovuto al fatto che se il valore per t non corrisponde a nessuno dei casi di commutazione, esso cadrà fuori dallo switch e pertanto il metodo non restituirà un valore. Tuttavia, hai dichiarato che restituirà una stringa. È necessario aggiungere un default in uno switch o un null ritorno:

enum Tile { Empty, White, Black }; 
    private string TileToString(Tile t) 
    { 
     switch (t) 
     { 
      case Tile.Empty: 
       return "."; 
      case Tile.White: 
       return "W"; 
      case Tile.Black: 
       return "B"; 
     } 
     return null; 
    } 
+0

"... se il valore per t non corrisponde a nessuno dei casi di commutazione ..." - questa è esattamente la mia domanda. * Come * non può corrispondere a nessuno dei casi di commutazione? – mpen

+0

Perché, come ha detto Jon, un Enum è in realtà solo una rappresentazione di un intero. E dato che puoi lanciare un numero intero all'enumerazione Tile, potresti tecnicamente passare un parametro non valido. – BeRecursive

1

Aggiungere il caso di default:

enum Tile { Empty, White, Black }; 
    private string TileToString(Tile t) 
    { 
     switch (t) 
     { 
      case Tile.Empty: 
       return "."; 
      case Tile.White: 
       return "W"; 
      case Tile.Black: 
       return "B"; 
      default: 
       return "."; 
     } 
    } 
+0

Conosco la soluzione, stavo solo cercando di capire * perché * questo è necessario. – mpen

+2

Un enum può essere esteso con nuovi valori in qualsiasi momento. Quindi è necessario il caso predefinito per intrappolarlo, in caso contrario si finirà con il codice che non copre ogni caso possibile. – Bart

22

Jon è naturalmente del tutto corretto che un enum può avere qualsiasi valore del suo sottostante digita, quindi lo switch non è esaustivo e pertanto esiste un percorso di codice che non restituisce. Tuttavia,, non è un'analisi completa del problema. Anche se il parametro fosse esaustivo, avresti comunque ricevuto l'errore.

Provalo:

int M(bool b) 
{ 
    switch(b) 
    { 
     case true : return 123; 
     case false: return 456; 
    } 
} 

O

int M(byte b) 
{ 
    switch(b) 
    { 
     case 0: case 1: case 2: ... all of them ... case 255: return 123; 
    } 
} 

In entrambi i casi, si otterrà lo stesso "punto raggiungibile fine a se il metodo non-vuoto" errore.

Questa è semplicemente una svista nella sezione "Verifica di raggiungibilità" della specifica C#. Definiamo il punto finale di un'istruzione switch come raggiungibile se non ha una sezione predefinita, punto. Non esiste una dispensa speciale per gli interruttori che consumano in modo esaustivo ogni possibile valore del loro input. È un caso d'angolo che i designer di lingue hanno mancato, e non è mai stato abbastanza prioritario per risolverlo.

Per gli altri tre numeri interessanti di analisi delle istruzioni switch, vedere:

http://ericlippert.com/2009/08/13/four-switch-oddities/

+0

Accidenti, voglio darti entrambi i controlli ora. Haha .. questo è abbastanza giusto. Ho pensato che questa fosse la vera ragione! – mpen

Problemi correlati