Vantaggi dell’utilizzo dell’operatore condizionale?: (Ternario)

Quali sono i vantaggi e gli svantaggi dell’operatore?: Al contrario dell’istruzione if-else standard. Gli ovvi sono:

Condizionale?: Operatore

  • Più breve e più conciso quando si tratta di confronti e assegnazioni di valori diretti
  • Non sembra flessibile come il costrutto if / else

Standard If / Else

  • Può essere applicato a più situazioni (come le chiamate di funzione)
  • Spesso sono inutilmente lunghi

La leggibilità sembra variare a seconda dell’affermazione. Per un po ‘di tempo dopo essere stato esposto all’operatore?: Mi ci è voluto un po’ di tempo per capire esattamente come funzionava. Consiglieresti di usarlo ovunque ansible, o di attenermi a if / else visto che lavoro con molti non programmatori?

Raccomanderei fondamentalmente di usarlo solo quando la dichiarazione risultante è estremamente breve e rappresenta un aumento significativo della concisione sull’equivalente if / else senza sacrificare la leggibilità.

Buon esempio:

 int result = Check() ? 1 : 0; 

Cattivo esempio:

 int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0; 

Questo è praticamente coperto dalle altre risposte, ma “è un’espressione” non spiega davvero perché sia ​​così utile …

In linguaggi come C ++ e C #, puoi definire campi readonly locali (all’interno di un corpo del metodo) che li usano. Questo non è ansible con una dichiarazione if / then convenzionale perché il valore di un campo readonly deve essere assegnato all’interno di quella singola istruzione:

 readonly int speed = (shiftKeyDown) ? 10 : 1; 

non è la stessa di:

 readonly int speed; if (shifKeyDown) speed = 10; // error - can't assign to a readonly else speed = 1; // error 

In un modo simile puoi incorporare un’espressione terziaria in un altro codice. Oltre a rendere il codice sorgente più compatto (e in alcuni casi più leggibile come risultato), può anche rendere il codice macchina generato più compatto ed efficiente:

 MoveCar((shiftKeyDown) ? 10 : 1); 

… può generare meno codice di dover chiamare lo stesso metodo due volte:

 if (shiftKeyDown) MoveCar(10); else MoveCar(1); 

Certo, è anche una forma più comoda e concisa (meno digitando, meno ripetizioni e può ridurre la possibilità di errori se devi duplicare blocchi di codice in un if / else). In casi di “modello comune” puliti come questo:

 object thing = (reference == null) ? null : reference.Thing; 

… è semplicemente più veloce leggere / analizzare / capire (una volta che ci si è abituati) rispetto all’equivalente long-wind se / else, in modo che possa aiutarti a “ingombrare” il codice più velocemente.

Certo, solo perché è utile non significa che sia la cosa migliore da usare in ogni caso. Ti consiglio di usarlo solo per brevi bit di codice dove il significato è chiaro (o reso più chiaro) usando ?: – se lo usi in un codice più complesso, o annidi operatori ternari l’uno nell’altro, può rendere il codice orribilmente difficile leggere.

Trovo particolarmente utile quando si esegue lo sviluppo Web se si desidera impostare una variabile su un valore inviato nella richiesta, se è definito o su un valore predefinito, se non lo è.

Di solito scelgo un operatore ternario quando altrimenti avrei un sacco di codice duplicato.

 if (a > 0) answer = compute(a, b, c, d, e); else answer = compute(-a, b, c, d, e); 

Con un operatore ternario, ciò potrebbe essere ottenuto con quanto segue.

 answer = compute(a > 0 ? a : -a, b, c, d, e); 

Un utilizzo davvero interessante è:

 x = foo ? 1 : bar ? 2 : baz ? 3 : 4; 

L’operatore condizionale è ottimo per le condizioni brevi, come questo:

 varA = boolB ? valC : valD; 

Lo uso occasionalmente perché ci vuole meno tempo per scrivere qualcosa in questo modo … sfortunatamente, questa ramificazione può a volte essere ignorata da un altro sviluppatore che esplora il tuo codice. Inoltre, il codice non è solitamente così breve, quindi di solito aiuto la leggibilità inserendo il? e: su linee separate, come questa:

 doSomeStuffToSomething(shouldSomethingBeDone() ? getTheThingThatNeedsStuffDone() : getTheOtherThingThatNeedsStuffDone()); 

Tuttavia, il grande vantaggio dell’uso dei blocchi if / else (e del perché li preferisco) è che è più facile entrare più tardi e aggiungere qualche logica aggiuntiva al ramo,

 if (shouldSomethingBeDone()) { doSomeStuffToSomething(getTheThingThatNeedsStuffDone()); doSomeAdditionalStuff(); } else { doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone()); } 

o aggiungi un’altra condizione:

 if (shouldSomethingBeDone()) { doSomeStuffToSomething(getTheThingThatNeedsStuffDone()); doSomeAdditionalStuff(); } else if (shouldThisOtherThingBeDone()){ doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone()); } 

Quindi, alla fine, si tratta di comodità per te ora (più breve da usare:?) Rispetto alla praticità per te (e altri) più tardi. È una sentenza … ma come tutti gli altri problemi di formattazione del codice, l’unica vera regola è quella di essere coerenti e di essere visivamente cortesi con chi deve mantenere (o valutare!) Il proprio codice.

(tutto il codice è stato compilato con gli occhi)

Una cosa da riconoscere quando si utilizza l’operatore ternario è un’espressione non un’affermazione.

Nei linguaggi funzionali come lo schema la distinzione non esiste:

(se (> ab) ab)

Condizionale?: Operatore “Non sembra flessibile come il costrutto if / else”

In lingue funzionali lo è.

Quando si programma in lingue imperative, si applica l’operatore ternario in situazioni in cui di solito utilizzo espressioni (assegnazione, istruzioni condizionali, ecc.).

Mentre le risposte sopra sono valide e sono d’accordo che la leggibilità sia importante, ci sono 2 ulteriori punti da considerare:

  1. In C # 6, puoi avere metodi con corpo espressivo.

Questo rende particolarmente conciso usare il ternario:

 string GetDrink(DayOfWeek day) => day == DayOfWeek.Friday ? "Beer" : "Tea"; 
  1. Il comportamento è diverso quando si tratta di conversione di tipo implicita.

Se hai i tipi T1 e T2 che possono entrambi essere convertiti implicitamente in T , allora il sotto non funziona:

 T GetT() => true ? new T1() : new T2(); 

(perché il compilatore tenta di determinare il tipo di espressione ternaria e non vi è alcuna conversione tra T1 e T2 ).

D’altra parte, la versione if/else qui sotto funziona:

 T GetT() { if (true) return new T1(); return new T2(); } 

perché T1 è convertito in T e quindi T2

A volte può rendere più facile leggere a prima vista l’assegnazione di un valore bool:

 // With button.IsEnabled = someControl.HasError ? false : true; // Without button.IsEnabled = !someControl.HasError; 

Se sto impostando un valore e so che sarà sempre una riga di codice per farlo, di solito uso l’operatore ternario (condizionale). Se c’è una possibilità che il mio codice e la logica cambieranno in futuro, io uso un if / else in quanto è più chiaro agli altri programmatori.

Di ulteriore interesse per voi potrebbe essere il ?? operatore .

Il vantaggio dell’operatore condizionale è che si tratta di un operatore. In altre parole, restituisce un valore. Poiché if è un’istruzione, non può restituire un valore.

Suggerirei di limitare l’uso dell’operatore ternario (? 🙂 alla semplice assegnazione di una riga se / altra logica. Qualcosa simile a questo modello:

 if() {  = ; } else {  = ; } 

Potrebbe essere facilmente convertito in:

  =  ?  : ; 

Eviterei di usare l’operatore ternario in situazioni che richiedono se / else se / else, nidificato se / else, o se / else logica di ramo che si traduce nella valutazione di più righe. L’applicazione dell’operatore ternario in queste situazioni potrebbe causare codice illeggibile, confuso e ingestibile. Spero che questo ti aiuti.

C’è qualche vantaggio sulle prestazioni dell’uso del? operatore in es. MS Visual C ++, ma questa è una cosa specifica per il compilatore. Il compilatore può effettivamente ottimizzare il ramo condizionale in alcuni casi.

Lo scenario che più mi trovo ad usarlo è per i valori predefiniti e soprattutto per i resi

 return someIndex < maxIndex ? someIndex : maxIndex; 

Questi sono davvero gli unici posti in cui trovo bello, ma per loro lo faccio.

Anche se stai cercando un booleano, a volte potrebbe sembrare una cosa appropriata da fare:

 bool hey = whatever < whatever_else ? true : false; 

Perché è così facile da leggere e capire, ma quell'idea dovrebbe essere sempre lanciata per il più ovvio:

 bool hey = (whatever < whatever_else); 

Se hai bisogno di più filiali nella stessa condizione, usa un se:

 if (A == 6) f(1, 2, 3); else f(4, 5, 6); 

Se hai bisogno di più rami con condizioni diverse, allora se il conteggio delle affermazioni dovesse aumentare a dismisura, ti consigliamo di utilizzare il ternario:

 f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 ); 

Inoltre, è ansible utilizzare l’operatore ternario durante l’inizializzazione.

 const int i = (A == 6)? 1 : 4; 

Farlo con se è molto disordinato:

 int i_temp; if (A == 6) i_temp = 1; else i_temp = 4; const int i = i_temp; 

Non è ansible inserire l’inizializzazione all’interno di if / else, poiché modifica l’ambito. Ma i riferimenti e le variabili const possono essere vincolati solo all’inizializzazione.

L’operatore ternario può essere incluso all’interno di un valore, mentre un if-then-else non può; d’altra parte, un if-then-else può eseguire loop e altre istruzioni, mentre l’operatore ternario può solo eseguire (eventualmente annullare) i rvalues.

Su una nota correlata, il && e || gli operatori consentono alcuni schemi di esecuzione che sono più difficili da implementare con if-then-else. Ad esempio, se uno ha diverse funzioni da chiamare e desidera eseguire un pezzo di codice se uno di essi fallisce, può essere fatto bene usando l’operatore &&. Farlo senza quell’operatore richiederà un codice ridondante, un goto o una variabile di flag extra.

Con C # 7 , è ansible utilizzare la nuova caratteristica dei ref locali per semplificare l’assegnazione condizionale delle variabili compatibili con ref. Quindi ora non solo puoi fare:

 int i = 0; T b = default(T), c = default(T); // initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾ ref T a = ref (i == 0 ? ref b : ref c); 

… ma anche estremamente meraviglioso:

 // assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals' (i == 0 ? ref b : ref c) = a; 

Quella riga di codice assegna il valore di a a boc, a seconda del valore di i .


Gli appunti
1. Il valore r è il lato destro di un compito, il valore che viene assegnato.
2. Il valore l è il lato sinistro di un compito, la variabile che riceve il valore assegnato.