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


101

Quali sono i vantaggi e gli svantaggi dell'operatore?: Rispetto all'istruzione if-else standard. Quelli ovvi sono:

Condizionale?: Operatore

  • Più breve e più conciso quando si tratta di confronti e assegnazioni di valori diretti
  • Non sembra essere 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 per ciascuno a seconda della dichiarazione. Per un po 'di tempo dopo essere stato esposto per la prima volta all'operatore?: Mi ci è voluto un po' di tempo per capire esattamente come funzionava. Consiglieresti di usarlo ove possibile, o di attenersi a if / else dato che lavoro con molti non programmatori?


8
Hai già capito il succo.
Byron Whitlock

1
@Nicholas Knight: Immagino che l'OP significhi che non puoi farlo, ad esempio, SomeCheck() ? DoFirstThing() : DoSecondThing();devi usare l'espressione per restituire un valore.
Dan Tao

6
Usalo dove è chiaro , resta con if / else se non lo è. La chiarezza del codice dovrebbe essere la tua considerazione principale.
hollsk

8
Hai visto '??' ancora? Scherzi a parte, se pensi che i ternari siano fighi ...
pdr

3
+1 per non chiamarlo semplicemente "l'operatore ternario" come fanno molti. Anche se è l'unico operatore ternario (al contrario di unario e binario) in C #, non è il suo nome.
John M Gant

Risposte:


122

Fondamentalmente consiglierei di usarlo solo quando l'affermazione risultante è estremamente breve e rappresenta un aumento significativo della concisione rispetto all'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;

5
Buona scelta, ma per la cronaca, è "concisione".
mqp


39
Inizio sempre con uno semplice e lo rendo più complesso nel tempo fino a renderlo completamente illeggibile.
Jouke van der Maas

9
La leggibilità nel secondo esempio potrebbe essere facilmente corretta con una migliore formattazione. Ma, come raccomanda l'OP, si tratta di leggibilità e concisione rispetto alla verbosità.
Nathan Ernst,

4
Non fa parte della domanda dell'OP, ma è importante notare il fatto che non puoi avere una returnparte del risultato dell'operazione ternaria. Ad esempio: check() ? return 1 : return 0;non funzionerà, ma return check() ? 1 : 0;funzionerà. È sempre divertente trovare queste piccole stranezze nella programmazione.
CSS

50

Questo è più o meno trattato dalle altre risposte, ma "è un'espressione" non spiega davvero perché sia ​​così utile ...

In linguaggi come C ++ e C #, puoi definire campi di sola lettura locali (all'interno del corpo di un metodo) utilizzandoli. Questo non è possibile con un'istruzione if / then convenzionale perché il valore di un campo di sola lettura deve essere assegnato all'interno di quella singola istruzione:

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

non è lo stesso di:

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

In modo simile è possibile incorporare un'espressione terziaria in un altro codice. Oltre a rendere il codice sorgente più compatto (e in alcuni casi più leggibile di conseguenza) può anche rendere il codice macchina generato più compatto ed efficiente:

MoveCar((shiftKeyDown) ? 10 : 1);

... può generare meno codice rispetto a dover chiamare lo stesso metodo due volte:

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

Naturalmente, è anche una forma più conveniente e concisa (meno battitura, meno ripetizioni e può ridurre la possibilità di errori se devi duplicare blocchi di codice in un if / else). In casi "pattern comuni" puliti come questo:

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

... è semplicemente più veloce da leggere / analizzare / comprendere (una volta che ci sei abituato) rispetto al prolisso equivalente if / else, quindi può aiutarti a 'grok' il codice più velocemente.

Certo, solo perché è utile non significa che sia la cosa migliore da usare in ogni caso. Consiglierei di usarlo solo per brevi bit di codice in cui 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 terribilmente difficile da leggere .


@JaminGrey "questo non significa che, quando viene creata la costante, sia impostata su 10 o 1." Vuoi dire fa mezzo che? I commenti errati possono causare più confusione ai nuovi programmatori C ++ rispetto al problema che stavi tentando di risolvere;)
Clonkex

5
Per i lettori futuri imbattersi in questo, dalla " velocità const int = (shiftKeyDown) 10: 1; ", che significa che quando la costante viene creato , esso è impostato su 10 o 1. non significa che ogni volta si accede alla costante che fa un controllo. (Solo nel caso in cui un nuovo programmatore C ++ fosse confuso)
Jamin Gray

2
... o per dirla in altro modo, a constè costante, cioè non può essere modificata dopo che l'istruzione in cui è dichiarata è stata eseguita.
Jason Williams

1
@JaminGrey. Non dovrebbe essere readonlyperò? Ho sempre pensato che constsignificasse " risolto in fase di compilazione e allineato ovunque usato ".
Nolonar

1
@ColinWiseman, è un esempio per illustrare come?: Può essere utilizzato. Affermo specificamente che solo perché puoi farlo, non significa che sia necessariamente la cosa "migliore" da fare in ogni caso specifico. Per risolverlo, il lettore dovrebbe usare il cervello ogni volta che incontra un caso in cui potrebbe essere utile.
Jason Williams,

14

Di solito scelgo un operatore ternario quando altrimenti avrei molto 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); 

12
personalmente avrei fatto aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);Soprattutto se b, c, de erichiesto un trattamento troppo.
corsiKa

10
Perché usare un condizionale in questo esempio? Prendi Abs (a) e chiama compute () una volta.
Ash

2
Sì, non ho creato il miglior esempio. :)
Ryan Bright

Per un principiante, questo non sembra equivalente. Non dovrebbe essere answer = compute (a> 0? A, b, c, d, e: -a, b, c, d, e); ?
pbreitenbach

@pbreitenbach: no - è una questione di precedenza - il primo argomento di compute(...)è a > 0 ? a : -1, che viene valutato separatamente dagli altri argomenti separati da virgole. Ad ogni modo, sfortunatamente al C ++ manca la notazione che la tua domanda pone per la gestione delle "tuple" di valori separati da virgole, quindi anche a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)è illegale, e non c'è niente di molto simile che funzioni senza modifiche a computese stesso.
Tony Delroy

12

Trovo particolarmente utile durante lo sviluppo web se voglio impostare una variabile su un valore inviato nella richiesta se è definita o su un valore predefinito se non lo è.


3
I valori predefiniti +1 in web dev sono un ottimo esempio di un buon posto per utilizzare l'operatore ternario
Byron Whitlock,

11

Un utilizzo davvero interessante è:

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

10
Fai attenzione con questo in PHP, l'operatore ternario si associa in modo sbagliato in PHP. Essenzialmente, se fooè falso, l'intera cosa valuterà a 4 senza fare gli altri test.
Tom Busby

4
@TomBusby - Wow. Ancora un altro motivo per odiare PHP, se sei qualcuno che già odia PHP.
Todd Lehman

6

L'operatore condizionale è ottimo per condizioni brevi, come questa:

varA = boolB ? valC : valD;

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

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

Tuttavia, il grande vantaggio nell'usare i blocchi if / else (e perché li preferisco) è che è più facile entrare in un secondo momento e aggiungere una 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:?) Contro convenienza per te (e altri) in seguito. È un giudizio ... ma come tutti gli altri problemi di formattazione del codice, l'unica vera regola è essere coerenti ed essere visivamente cortesi con coloro che devono mantenere (o valutare!) Il tuo codice.

(tutto il codice eye-compilato)


5

Una cosa da riconoscere quando si usa l'operatore ternario è che è un'espressione, non un'affermazione.

In linguaggi funzionali come lo schema la distinzione non esiste:

(se (> ab) ab)

Condizionale?: Operatore "Non sembra essere flessibile come il costrutto if / else"

Nei linguaggi funzionali lo è.

Quando si programma in linguaggi imperativi, applico l'operatore ternario in situazioni in cui normalmente utilizzerei espressioni (assegnazione, istruzioni condizionali, ecc.).


5

Sebbene le risposte di cui sopra siano valide e concordo con la leggibilità come importante, ci sono altri 2 punti da considerare:

  1. In C # 6 è possibile avere metodi con corpo di espressione.

Ciò rende particolarmente conciso l'uso del ternario:

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

Se si dispone di tipi T1e T2che possono essere entrambi convertiti in modo implicito T, quanto segue non funziona:

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

(perché il compilatore cerca di determinare il tipo di espressione ternaria e non c'è conversione tra T1e T2.)

D'altra parte, la if/elseversione seguente funziona:

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

perché T1si converte in Te così èT2


5

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

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

// Without
button.IsEnabled = !someControl.HasError;

4

Se sto impostando un valore e so che sarà sempre una riga di codice per farlo, di solito utilizzo l'operatore ternario (condizionale). Se c'è una possibilità che il mio codice e la mia logica cambino in futuro, utilizzo un if / else perché è più chiaro per gli altri programmatori.

Di ulteriore interesse per te potrebbe essere ?? operatore .


4

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


4

Suggerirei di limitare l'uso dell'operatore ternario (? :) alla semplice logica di assegnazione a riga singola if / else. Qualcosa che assomigli a questo modello:

if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

Potrebbe essere facilmente convertito in:

<variable> = <boolCondition> ? <value> : <anotherValue>;

Eviterei di utilizzare l'operatore ternario in situazioni che richiedono if / else if / else, nidificato if / else o la logica di branch if / else che si traduce nella valutazione di più righe. L'applicazione dell'operatore ternario in queste situazioni risulterebbe probabilmente in codice illeggibile, confuso e ingestibile. Spero che questo ti aiuti.


2

C'è qualche vantaggio in termini di prestazioni nell'usare il? operatore in es. MS Visual C ++, ma questa è davvero una cosa specifica del compilatore. Il compilatore può effettivamente ottimizzare il ramo condizionale in alcuni casi.


2

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

return someIndex < maxIndex ? someIndex : maxIndex;

Quelli sono davvero gli unici posti in cui lo trovo carino, ma per loro sì.

Tuttavia, se stai cercando un valore 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 sempre essere lanciata per il più ovvio:

bool hey = (whatever < whatever_else);

2

Se hai bisogno di più rami alla stessa condizione, usa un if:

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

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

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

Inoltre, è possibile utilizzare l'operatore ternario nell'inizializzazione.

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

Farlo con if è molto complicato:

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

Non puoi mettere l'inizializzazione all'interno di if / else, perché cambia l'ambito. Ma i riferimenti e le variabili const possono essere vincolati solo durante l'inizializzazione.


2

L'operatore ternario può essere incluso all'interno di un rvalue, mentre un if-then-else non può; d'altra parte, un if-then-else può eseguire cicli e altre istruzioni, mentre l'operatore ternario può eseguire solo rvalues ​​(possibilmente nulli).

In una nota correlata, && e || gli operatori consentono alcuni modelli di esecuzione che sono più difficili da implementare con if-then-else. Ad esempio, se si hanno diverse funzioni da chiamare e si desidera eseguire un pezzo di codice se una di esse fallisce, può essere fatto bene usando l'operatore &&. Farlo senza quell'operatore richiederà un codice ridondante, un goto o una variabile flag aggiuntiva.


1

Con C # 7 , è possibile usare la nuova funzionalità ref locals 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 aa bo c, a seconda del valore di i.



Note
1. Il valore r è il lato destro di un compito, il valore che viene assegnato.
2. l-value è la sinistra lato -hand di un'assegnazione, la variabile che riceve il valore assegnato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.