invocazione di metodi all'interno di un'espressione condizionale

4

In uno spesso citato (se datato) riferimento per gli standard di codifica C # (pdf ; da Lance Hunt), la dichiarazione è fatta,

33. Avoid invoking methods within a conditional expression.

Non ho visto questa raccomandazione altrove, incluso nelle linee guida .NET ( qui o qui ). A differenza delle altre linee guida molto ragionevoli contenute nel documento di Hunt, questo suggerimento mi sembra non intuitivo. Qualcuno può fornire una giustificazione per questo?

    
posta kmote 16.06.2014 - 18:08
fonte

4 risposte

9

Bene, non ho letto il documento di Hunt per intero ma sembra che il suo consiglio sia principalmente sulla leggibilità. Una dichiarazione come ...

if(myObject1.functionA() && myObject1.functionB())
{
  // do something
}

... ha un certo rischio di essere frainteso, a causa della valutazione di cortocircuito e anche a causa della lunghezza della linea. Invece ...

bool resultOfA = myObject1.functionA();
bool resultOfB = myObject1.functionB();

if(resultOfA && resultOfB)
{
  // do something
}

... è più facile da leggere e chiamerà sempre entrambe le funzioni (). Naturalmente, se si desidera esplicitamente utilizzare la valutazione di cortocircuito (forse perché la funzioneB non dovrebbe essere chiamata quando la funzioneA restituisce false), potrebbe essere meglio ignorare le regole di Hunt (nota: dice "evita", non "non farlo in nessuna circostanza").

    
risposta data 16.06.2014 - 18:25
fonte
5

Dovresti anche prendere in considerazione le chiamate di funzione che hanno effetti collaterali (ad esempio cambiare alcune variabili globali). Questo può essere un problema più importante della semplice leggibilità.

if (funcA() && funcB()) ...

Potrebbe essere previsto che funcB venga chiamato solo quando funcA ha successo, ma potresti anche fare un errore qui. Se entrambe le funzioni hanno effetti collaterali e entrambi sono necessari per essere eseguiti, il codice sopra riportato è logicamente errato. Ciò potrebbe accadere perché il programmatore era troppo ambizioso per mantenere stretto il suo codice, ma potrebbe anche essere che fosse corretto prima, ma a causa di alcune modifiche da qualche altra parte le ipotesi prese non sono più valide.

Quindi non è mai una buona idea chiamare le funzioni con effetti collaterali all'interno di un'espressione booleana.

    
risposta data 16.06.2014 - 23:36
fonte
4

Innanzitutto, è chiaro dal contesto che per conditional expression Hunt si intende l'espressione booleana in un'istruzione condizionale. Questa è una sezione sul controllo del flusso, e non sull'uso di espressioni booleane per altri scopi, o su (come direi) espressioni condizionali basate sull'operatore '?:' Ternario.

Credo che l'intento sottostante qui sia che le parti di un'espressione condizionale usate per il controllo del flusso dovrebbero evitare equivoci che potrebbero derivare dalla valutazione del cortocircuito o da effetti collaterali. Non vedo alcun motivo per evitare di chiamare le funzioni del metodo per ottenere valori usati in un'espressione condizionale (ammesso che non ci siano effetti collaterali), e non vedo alcun problema a seconda della valutazione del cortocircuito a condizione che le relazioni tra le parti dell'espressione siano chiaramente visibile. È il rischio di connessioni nascoste e conseguenze nascoste che dovrebbero essere evitate. Quindi:

if (x.Contains(a) && x.Median() > b) {} // ok
if (x.Insert(a) && x.Median() > b) {} // NOT ok

C'è un'ulteriore considerazione. La chiamata di un metodo può comportare una penalizzazione delle prestazioni, quindi se Median () è lento a calcolare:

if (x.Median() >= a && x.Median() <= b) {} // slower
var median = x.Median();
if (median >= a && median <= b) {} // faster

Detto questo, penso che Hunt ci offenda non riuscendo a spiegare cosa intende. Speriamo che una futura versione risolva questa lacuna.

    
risposta data 17.06.2014 - 05:41
fonte
3

Incoraggia il caricamento di più chiamate di funzione nel condizionale, quindi fare affidamento sulla valutazione di cortocircuito per gestire la ramificazione.

Quando ciò accade, il programma diventa più difficile da eseguire il debug. Se hai 5 funzioni all'interno di un singolo condizionale e tutto quello che sai è che il condizionale ha fallito ... Poi sei costretto a eseguire il debug dei condizionali, o punti di rottura dello spam per vedere cosa viene effettivamente chiamato. In effetti, è probabilmente più veloce da refactare in quel punto e posizionare ogni chiamata di funzione sulla propria linea.

All'inizio sembra innocuo, e sì risparmierai le linee. Ma quando lavori su un grande progetto caricato con questo stile, o il tuo progetto cresce e stai usando questo stile, aggiungerà tempo alle tue attività di debug. Dovresti programmare come se dovesse rompersi, perché si spezzerà.

    
risposta data 29.03.2016 - 18:25
fonte

Leggi altre domande sui tag