2012-11-11 7 views
11

Considerate questa funzione, che si può pensare come una tabella di verità:Come posso evitare uno stato booleano impossibile in C#?

public Foo doSomething(bool a, bool b) { 

     if (a && b) return doAB(); 
    else if (a && !b) return doA(); 
    else if (!a && b) return doB(); 
    else if (!a && !b) return doNotANotB(); 

    else throw new Exception("Well done, you defeated boolean logic!"); 
} 

Il compilatore insiste su quell'ultimo else clausola. Ma dal punto di vista di una tavola di verità, quello è uno stato impossibile.

Sì, funziona, e sì, posso conviverci. Ma mi chiedo se c'è qualche meccanismo in C# per evitare questo tipo di codice, o se mi sono perso qualcosa di ovvio?

UPDATE:
per i punti bonus, e per pura curiosità, ci sono tutte le lingue che si occupano di questo genere di cose in modo diverso? Forse non è un problema linguistico, ma piuttosto uno di un compilatore intelligente (ma i casi limite sarebbero inimmaginabilmente complicati, suppongo).

+6

Sì, lì ho s tale meccanismo: basta semplicemente cancellare la condizione dell'ultimo 'if'. – dasblinkenlight

+0

Molto vero! Pun intended. Ma se possibile vorrei che il codice fosse "leggibile" o "espressivo". Tieni presente che questo è un semplice esempio. E tra 7 mesi mi starò grattando la testa chiedendomi cosa stavo pensando quando l'ho scritto (quindi la preferenza per il codice espressivo). –

+0

Se le azioni non sono correlate l'una con l'altra, non userei il pattern 'if/else if', ma preferisco andare su quattro separati blocchi' se'' dopo aver lanciato l'eccezione per soddisfare il compilatore. Nella maggior parte dei casi, tuttavia, esiste un'azione "predefinita" che si adatta perfettamente a un 'else' vuoto. –

risposta

18

Considerando la tabella di verità, l'ultima condizione è del tutto superflua. Esso può essere lasciato cadere senza alterare la logica del programma, in questo modo:

public MyType doSomething(bool a, bool b) { 

     if (a && b) return doAB(); 
else if (a && !b) return doA(); 
else if (!a && b) return doB(); 
else/*if (!a && !b)*/ return doNotANotB(); 
} 

Ora avete un finale catch-all if, e il compilatore è felice. Non è necessario rimuovere completamente la condizione: spesso trovo una buona idea tenerla in un commento per la leggibilità.

+3

Mi piace davvero lasciare il codice commentato perché documenta in modo efficace la condizione dell'ultimo ramo. +1 – usr

+0

Sì, come ho notato nel commento sopra, questo è vero. Ma c'è un modo in cui posso esprimere l'intenzione senza ricorrere a condizioni implicite? Probabilmente no ... –

+0

@BobbyB Assolutamente, non abbiate fretta! C'è una possibilità che qualcuno che conosce il compilatore C# dall'interno veda questa domanda e condivida con noi una risposta perspicace. (Voglio dire in particolare [Eric Lippert] (http://stackoverflow.com/users/88656/eric-lippert), anche se, purtroppo, ultimamente non ha risposto a troppe domande). – dasblinkenlight

2
public MyType doSomething(bool a, bool b) 
     { 
      switch(a) 
      { 
       case true: 
        if (b) return doAB(); 
        return doA(); 
       default: 
        if (b) return doB(); 
        return doNotANotB(); 

      } 

     } 

Aggiornamento:

Nota che la dichiarazione originale è in realtà:

public MyType doSomething(bool a, bool b) 
     { 
      if (a && b) return doAB(); 
      if (a) return doA(); 
      if (b) return doB(); 
      return doNotANotB(); 
     } 

per divertimento e succintnes (se non la leggibilità: p):

static MyType doSomething(bool a, bool b) 
     { 
      return a && b ? doAB() : a ? doA() : b ? doB() : doNotANotB(); 
     } 
+0

Ovviamente meno leggibile, ma davvero fantastico! –

+0

Penso di preferire l'ultimo esempio. Non penso che sia meno leggibile a patto che vengano usati nomi di variabili e metodi appropriati, e forse qualche newline e indentazione. –

+0

* Nods *, mi piace da solo, anche se penso che sia facile inciampare nelle valutazioni ternarie annidate. – Anthill

6

Prova F #. Se è in grado di rilevare una condizione esaustiva corrispondente alla sua direttiva di corrispondenza, non richiede un altro.

http://ganesansenthilvel.blogspot.co.at/2011/12/f-pattern-matching.html?m=1#!

> let testAND x y = 
match x, y with 
| true, true -> true 
| true, false -> false 
| false, true -> false 
| false, false -> true 

> testAND true false;; 
val it: bool = true 

e per una specifica incompleta

> let testAND x y = 
match x, y with 
| true, true -> true 
// Commented | true, false -> false 
| false, true -> false 
| false, false -> true 
> testAND true false;; 

il compilatore dirà

Microsoft.Fsharp.Core.MatchFailureExcption: The match cases were incomplete at:.... 
Stopped due to error 
7
if(a) return b ? doAB() : doA(); 
else return b ? doB() : doNotAnotB(); 

o più corto:

return a ? (b ? doAB() : doA()) 
     : (b ? doB() : doNotAnotB());