Beneficios de usar el operador condicional?: (Ternario)

101

¿Cuáles son las ventajas y desventajas del operador?: En contraposición a la declaración estándar if-else. Los obvios son:

Condicional?: Operador

  • Más corto y conciso cuando se trata de asignaciones y comparaciones de valores directos
  • No parece ser tan flexible como el constructo if / else

Estándar If / Else

  • Puede aplicarse a más situaciones (como llamadas a funciones)
  • A menudo son innecesariamente largas

La legibilidad parece variar para cada uno dependiendo de la declaración. Durante un tiempo, después de haber estado expuesto por primera vez al operador?:, Me tomó un tiempo digerir exactamente cómo funcionaba. ¿Recomendaría usarlo siempre que sea posible, o ceñirse a if / else dado que trabajo con muchos no programadores?

KChaloux
fuente
8
Ya entendiste la esencia.
Byron Whitlock
1
@Nicholas Knight: Supongo que OP significa que no puede hacer, por ejemplo, SomeCheck() ? DoFirstThing() : DoSecondThing();tiene que usar la expresión para devolver un valor.
Dan Tao
6
Úselo donde esté claro , quédese con si / de lo contrario si no lo es. La claridad del código debe ser su principal consideración.
hollsk
8
Haz visto '??' ¿todavía? En serio, si crees que los ternaries son geniales ...
pdr
3
+1 por no llamarlo simplemente "el operador ternario" como muchos lo hacen. Aunque es el único operador ternario (a diferencia de unario y binario) en C #, ese no es su nombre.
John M Gant

Respuestas:

122

Básicamente, recomendaría usarlo solo cuando la declaración resultante sea extremadamente corta y represente un aumento significativo en la concisión sobre el equivalente if / else sin sacrificar la legibilidad.

Buen ejemplo:

int result = Check() ? 1 : 0;

Mal ejemplo:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;
Dan Tao
fuente
5
Buena decisión, pero para que conste, eso es "concisión".
mqp
6
@mquander, ¿estás seguro de eso? merriam-webster.com/dictionary/concise
Byron Whitlock
39
Siempre comienzo con uno simple y lo hago más complejo con el tiempo hasta que es completamente ilegible.
Jouke van der Maas
9
La legibilidad en el segundo ejemplo podría corregirse fácilmente con un mejor formato. Pero, como recomienda el OP, todo se reduce a la legibilidad y la concisión frente a la verbosidad.
Nathan Ernst
4
No es parte de la pregunta del OP, pero es importante tener en cuenta el hecho de que no puede returnser parte del resultado de la operación ternaria. Por ejemplo: check() ? return 1 : return 0;no funcionará, pero return check() ? 1 : 0;sí. Siempre es divertido encontrar estas pequeñas peculiaridades en la programación.
CSS
50

Esto está prácticamente cubierto por las otras respuestas, pero "es una expresión" realmente no explica por qué es tan útil ...

En lenguajes como C ++ y C #, puede definir campos de solo lectura locales (dentro del cuerpo de un método) usándolos. Esto no es posible con una declaración if / then convencional porque el valor de un campo de solo lectura debe asignarse dentro de esa declaración única:

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

no es lo mismo que:

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

De manera similar, puede incrustar una expresión terciaria en otro código. Además de hacer que el código fuente sea más compacto (y en algunos casos más legible como resultado), también puede hacer que el código máquina generado sea más compacto y eficiente:

MoveCar((shiftKeyDown) ? 10 : 1);

... puede generar menos código que tener que llamar al mismo método dos veces:

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

Por supuesto, también es una forma más conveniente y concisa (menos escritura, menos repetición y puede reducir la posibilidad de errores si tiene que duplicar fragmentos de código en un if / else). En casos limpios de "patrón común" como este:

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

... es simplemente más rápido de leer / analizar / comprender (una vez que esté acostumbrado) que el equivalente if / else de largo aliento, por lo que puede ayudarlo a 'asimilar' el código más rápido.

Por supuesto, el hecho de que sea útil no significa que sea lo mejor para usar en todos los casos. Aconsejaría usarlo solo para fragmentos cortos de código donde el significado es claro (o se aclara más) al usarlo ?:: si lo usa en un código más complejo, o anida operadores ternarios entre sí, puede hacer que el código sea terriblemente difícil de leer .

Jason Williams
fuente
@JaminGrey "eso no significa que, cuando se crea la constante, se establece en 10 o 1." ¿Quieres decir que significa eso? Los comentarios incorrectos pueden causar más confusión a los nuevos programadores de C ++ que el problema que intentaba
solucionar
5
Para los lectores futuros que viene a través de este, por " la velocidad const int = (shiftKeyDown) 10: 1; ", que significa que cuando la constante se crea por primera vez , se establece en 10 o 1. Es no quiere decir que cada vez se accede a la constante hace una comprobación. (Solo en caso de que un programador de C ++ más nuevo estuviera confundido)
Jamin Gray
2
... o dicho de otra manera, a constes constante, es decir, no se puede cambiar después de que se haya ejecutado la sentencia en la que se declaró.
Jason Williams
1
@JaminGrey. ¿No debería ser así readonly? Siempre pensé que constsignificaba " resuelto en tiempo de compilación y alineado donde sea que se use ".
Nolonar
1
@ColinWiseman, es un ejemplo para ilustrar cómo?: Puede usarse. Específicamente afirmo que el hecho de que pueda hacerlo no significa que sea necesariamente lo "mejor" que se puede hacer en un caso específico. Para resolverlo, se espera que el lector utilice su cerebro cada vez que se encuentre con un caso en el que pueda resultarle útil.
Jason Williams
14

Por lo general, elijo un operador ternario cuando, de lo contrario, tendría mucho código duplicado.

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

Con un operador ternario, esto se podría lograr con lo siguiente.

answer = compute(a > 0 ? a : -a, b, c, d, e); 
Ryan Bright
fuente
12
personalmente me gustaría hacer aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);todo si b, c, dy erequiere tratamiento demasiado.
corsiKa
10
¿Por qué usar un condicional en este ejemplo? Simplemente obtenga Abs (a) y llame a compute () una vez.
Ash
2
Sí, no creé el mejor ejemplo. :)
Ryan Bright
Para un novato, eso no parece equivalente. ¿No tendría que ser answer = compute (a> 0? A, b, c, d, e: -a, b, c, d, e); ?
pbreitenbach
@pbreitenbach: no, es una cuestión de precedencia, el primer argumento de compute(...)es a > 0 ? a : -1, que se evalúa por separado de los otros argumentos separados por comas. De todos modos, desafortunadamente C ++ carece de la notación que plantea su pregunta para manejar "tuplas" de valores separados por comas, por lo que incluso a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)es ilegal y no hay nada muy similar que funcione sin cambios en computesí mismo.
Tony Delroy
12

Lo encuentro particularmente útil cuando hago desarrollo web si quiero establecer una variable en un valor enviado en la solicitud si está definido o en algún valor predeterminado si no lo está.

wshato
fuente
3
Los valores predeterminados de +1 en el desarrollo web son un excelente ejemplo de un buen lugar para usar el operador ternario
Byron Whitlock
11

Un uso realmente genial es:

x = foo ? 1 :
    bar ? 2 :
    baz ? 3 :
          4;
aib
fuente
10
Tenga cuidado con esto en PHP, el operador ternario se asocia de manera incorrecta en PHP. Esencialmente, si fooes falso, entonces todo se evaluará a 4 sin hacer las otras pruebas.
Tom Busby
4
@TomBusby - Vaya. Otra razón más para odiar PHP, si eres alguien que ya odia PHP.
Todd Lehman
6

El operador condicional es ideal para condiciones breves, como esta:

varA = boolB ? valC : valD;

Lo uso de vez en cuando porque toma menos tiempo escribir algo de esa manera ... desafortunadamente, esta ramificación a veces puede ser pasada por alto por otro desarrollador que navega por su código. Además, el código no suele ser tan corto, por lo que suelo ayudar a la legibilidad poniendo el? y: en líneas separadas, como esta:

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

Sin embargo, la gran ventaja de usar bloques if / else (y por qué los prefiero) es que es más fácil entrar más tarde y agregar algo de lógica adicional a la rama,

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

o agregue otra condición:

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

Entonces, al final, se trata de conveniencia para usted ahora (más corto de usar:?) Versus conveniencia para usted (y otros) más adelante. Es una cuestión de criterio ... pero como todos los demás problemas de formato de código, la única regla real es ser coherente y ser visualmente cortés con quienes tienen que mantener (¡o calificar!) Su código.

(todo el código compilado a ojo)

iandisme
fuente
5

Una cosa que hay que reconocer cuando se usa el operador ternario es que es una expresión, no una declaración.

En lenguajes funcionales como el esquema, la distinción no existe:

(si (> ab) ab)

Condicional?: Operador "No parece ser tan flexible como la construcción if / else"

En lenguajes funcionales lo es.

Al programar en lenguajes imperativos, aplico el operador ternario en situaciones en las que normalmente usaría expresiones (asignación, declaraciones condicionales, etc.).

Ken Struys
fuente
5

Si bien las respuestas anteriores son válidas y estoy de acuerdo con que la legibilidad es importante, hay 2 puntos más a considerar:

  1. En C # 6, puede tener métodos con cuerpo de expresión.

Esto hace que sea particularmente conciso el uso del ternario:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. El comportamiento difiere cuando se trata de conversión de tipo implícita.

Si tiene tipos T1y T2ambos pueden convertirse implícitamente a T, entonces lo siguiente no funciona:

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

(porque el compilador intenta determinar el tipo de expresión ternaria y no hay conversión entre T1y T2).

Por otro lado, la if/elseversión siguiente funciona:

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

porque T1se convierte ay TtambiénT2

la-yumba
fuente
5

A veces, puede hacer que la asignación de un valor bool sea más fácil de leer a primera vista:

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

// Without
button.IsEnabled = !someControl.HasError;
Tyler Pantuso
fuente
4

Si estoy estableciendo un valor y sé que siempre será una línea de código para hacerlo, normalmente uso el operador ternario (condicional). Si existe la posibilidad de que mi código y mi lógica cambien en el futuro, uso un if / else ya que es más claro para otros programadores.

De mayor interés para usted puede ser el ?? operador .

drharris
fuente
4

La ventaja del operador condicional es que es un operador. En otras palabras, devuelve un valor. Dado que ifes una declaración, no puede devolver un valor.

Gabe
fuente
4

Recomendaría limitar el uso del operador ternario (? :) a una simple asignación de una sola línea lógica if / else. Algo parecido a este patrón:

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

Se puede convertir fácilmente a:

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

Evitaría usar el operador ternario en situaciones que requieran if / else if / else, anidado if / else, o if / else rama lógica que da como resultado la evaluación de múltiples líneas. La aplicación del operador ternario en estas situaciones probablemente resultaría en un código ilegible, confuso e inmanejable. Espero que esto ayude.

HOCA
fuente
2

Hay algún beneficio de rendimiento al usar el? operador en, por ejemplo. MS Visual C ++, pero esto es realmente una cosa específica del compilador. El compilador puede optimizar la rama condicional en algunos casos.

darklon
fuente
2

El escenario que más me encuentro usándolo es para valores predeterminados y especialmente en devoluciones

return someIndex < maxIndex ? someIndex : maxIndex;

Esos son realmente los únicos lugares en los que me parece agradable, pero para ellos sí.

Aunque si está buscando un booleano, esto a veces puede parecer algo apropiado para hacer:

bool hey = whatever < whatever_else ? true : false;

Porque es muy fácil de leer y entender, pero esa idea siempre debe descartarse por lo más obvio:

bool hey = (whatever < whatever_else);
Jimmy Hoffa
fuente
2

Si necesita varias ramas en la misma condición, use un if:

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

Si necesita varias ramas con diferentes condiciones, entonces, si el recuento de declaraciones aumentaría, querrá usar el ternario:

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

Además, puede utilizar el operador ternario en la inicialización.

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

Hacer eso con if es muy complicado:

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

No puede poner la inicialización dentro de if / else, porque cambia el alcance. Pero las referencias y las variables const solo se pueden vincular en la inicialización.

Ben Voigt
fuente
2

El operador ternario puede incluirse dentro de un rvalue, mientras que un if-then-else no puede; por otro lado, un if-then-else puede ejecutar bucles y otras declaraciones, mientras que el operador ternario solo puede ejecutar (posiblemente anular) rvalues.

En una nota relacionada, && y || Los operadores permiten algunos patrones de ejecución que son más difíciles de implementar con if-then-else. Por ejemplo, si uno tiene varias funciones para llamar y desea ejecutar un fragmento de código si alguna de ellas falla, se puede hacer muy bien usando el operador &&. Hacerlo sin ese operador requerirá un código redundante, un goto o una variable de bandera adicional.

Super gato
fuente
1

Con C # 7 , puede usar la nueva característica de locales de referencia para simplificar la asignación condicional de variables compatibles con ref. Así que ahora, no solo puedes hacer:

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);

... pero también lo extremadamente maravilloso:

// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'

(i == 0 ? ref b : ref c) = a;

Esa línea de código asigna el valor de aa bo c, según el valor de i.



Notas
1. El valor r es el lado derecho de una asignación, el valor que se asigna.
2. El valor l es el lado izquierdo de una asignación, la variable que recibe el valor asignado.

Glenn Slayden
fuente