Benefícios do uso do operador condicional?: (Ternário)

Quais são os benefícios e desvantagens do operador?: Em oposição à instrução padrão if-else. Os mais óbvios são:

Condicional: Operador

  • Mais curto e mais conciso ao lidar com comparações de valor direto e atribuições
  • Não parece ser tão flexível quanto a construção if / else

Padrão Se / Outro

  • Pode ser aplicado a mais situações (como chamadas de function)
  • Muitas vezes são desnecessariamente longos

A legibilidade parece variar para cada um, dependendo da afirmação. Por um tempo depois de ter sido exposto ao operador?: Demorei um pouco para digerir exatamente como funcionava. Você recomendaria usá-lo sempre que possível, ou permanecendo em if / else, dado que eu trabalho com muitos não-programadores?

Eu basicamente recomendo usá-lo somente quando a declaração resultante é extremamente curta e representa um aumento significativo na concisão sobre o equivalente if / else sem sacrificar a legibilidade.

Bom exemplo:

 int result = Check() ? 1 : 0; 

Mau exemplo:

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

Isso é bastante coberto por outras respostas, mas “é uma expressão” não explica realmente por que isso é tão útil …

Em linguagens como C ++ e C #, você pode definir campos readonly locais (dentro de um corpo de método) usando-os. Isso não é possível com uma instrução convencional se / então porque o valor de um campo readonly deve ser atribuído dentro dessa instrução única:

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

não é o mesmo que:

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

De maneira semelhante, você pode incorporar uma expressão terciária em outro código. Além de tornar o código-fonte mais compacto (e, em alguns casos, mais legível como resultado), ele também pode tornar o código de máquina gerado mais compacto e eficiente:

 MoveCar((shiftKeyDown) ? 10 : 1); 

… pode gerar menos código do que ter que chamar o mesmo método duas vezes:

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

É claro que também é uma forma mais conveniente e concisa (menos digitação, menos repetição e pode reduzir a chance de erros se você tiver que duplicar pedaços de código em um if / else). Em casos de “padrão comum” limpo como este:

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

… é simplesmente mais rápido ler / analisar / entender (uma vez que você está acostumado a isso) do que o prolixo if / else equivalente, então ele pode ajudá-lo a “grok” código mais rápido.

Claro, só porque é útil não significa que é a melhor coisa para usar em todos os casos. Eu aconselharia apenas usá-lo para pequenos trechos de código onde o significado é claro (ou tornado mais claro) usando ?: – se você usá-lo em código mais complexo, ou aninhar operadores ternários dentro um do outro, pode tornar o código horrivelmente difícil ler.

Eu acho particularmente útil ao fazer desenvolvimento web se eu quiser definir uma variável para um valor enviado na requisição se ela estiver definida ou para algum valor padrão, se não for.

Eu geralmente escolho um operador ternário quando eu tenho um monte de código duplicado.

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

Com um operador ternário, isso poderia ser feito com o seguinte.

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

Um uso muito legal é:

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

O operador condicional é ótimo para condições curtas, como esta:

 varA = boolB ? valC : valD; 

Eu uso isso ocasionalmente porque leva menos tempo para escrever algo dessa maneira … infelizmente, essa ramificação às vezes pode ser perdida por outro desenvolvedor navegando pelo seu código. Além disso, o código geralmente não é tão curto, então eu geralmente ajudo a legibilidade colocando o código? e: em linhas separadas, assim:

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

No entanto, a grande vantagem de usar blocos if / else (e porque eu os prefiro) é que é mais fácil entrar mais tarde e adicionar alguma lógica adicional ao branch,

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

ou adicione outra condição:

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

Então, no final, é sobre conveniência para você agora (menor para usar:?) Vs. conveniência para você (e outros) mais tarde. É uma chamada de julgamento … mas, como todos os outros problemas de formatação de código, a única regra real é ser consistente e ser visualmente cortês com aqueles que precisam manter (ou corrigir!) Seu código.

(todo código compilado pelo olho)

Uma coisa a reconhecer ao usar o operador ternário é que é uma expressão e não uma declaração.

Em linguagens funcionais como esquema a distinção não existe:

(se (> ab) ab)

Condicional ?: Operador “Não parece ser tão flexível quanto a construção if / else”

Em linguagens funcionais é.

Quando estou programando em linguagens imperativas, aplico o operador ternário em situações em que normalmente usaria expressões (atribuição, declarações condicionais, etc.).

Embora as respostas acima sejam válidas e eu concorde com a legibilidade sendo importante, há mais dois pontos a serem considerados:

  1. Em C # 6, você pode ter methods encorpados de expressão.

Isso torna particularmente conciso o uso do ternário:

 string GetDrink(DayOfWeek day) => day == DayOfWeek.Friday ? "Beer" : "Tea"; 
  1. O comportamento difere quando se trata de conversão de tipo implícita.

Se você tiver os tipos T1 e T2 que podem ser implicitamente convertidos em T , o seguinte não funciona:

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

(porque o compilador tenta determinar o tipo da expressão ternária e não há conversão entre T1 e T2 ).

Por outro lado, a versão if/else abaixo funciona:

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

porque T1 é convertido em T e assim é T2

Às vezes, pode facilitar a leitura de um valor de bool à primeira vista:

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

Se eu estou configurando um valor e sei que sempre será uma linha de código, geralmente uso o operador ternário (condicional). Se houver uma chance de meu código e lógica mudarem no futuro, eu uso um if / else, pois é mais claro para outros programadores.

De mais interesse para você pode ser o ?? operador .

A vantagem do operador condicional é que ele é um operador. Em outras palavras, ele retorna um valor. Como if é uma instrução, ela não pode retornar um valor.

Eu recomendaria limitar o uso do operador ternário (? 🙂 à atribuição de linha única simples se / mais lógica. Algo parecido com esse padrão:

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

Poderia ser facilmente convertido para:

  =  ?  : ; 

Eu evitaria usar o operador ternário em situações que exigem if / else if / else, nested if / else, ou se / mais lógica de ramificação que resulta na avaliação de várias linhas. A aplicação do operador ternário nessas situações provavelmente resultaria em código ilegível, confuso e incontrolável. Espero que isto ajude.

Existe algum benefício de desempenho de usar o? operador em por exemplo. MS Visual C ++, mas isso é realmente uma coisa específica do compilador. O compilador pode realmente otimizar o ramo condicional em alguns casos.

O cenário que mais me encontro usando é para valores padrão e especialmente em retornos

 return someIndex < maxIndex ? someIndex : maxIndex; 

Esses são realmente os únicos lugares que eu acho legal, mas para eles eu acho.

No entanto, se você estiver procurando por um booleano, isso pode parecer algo apropriado:

 bool hey = whatever < whatever_else ? true : false; 

Porque é tão fácil de ler e entender, mas essa ideia deve sempre ser lançada para o mais óbvio:

 bool hey = (whatever < whatever_else); 

Se você precisar de várias ramificações na mesma condição, use um if:

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

Se você precisar de várias ramificações com condições diferentes, se a contagem de instruções tiver uma bola de neve, você deverá usar o ternário:

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

Além disso, você pode usar o operador ternário na boot.

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

Fazer isso com o if é muito confuso:

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

Você não pode colocar a boot dentro do if / else, porque ele altera o escopo. Mas referências e variables ​​const só podem ser ligadas na boot.

O operador ternário pode ser incluído dentro de um rvalue, enquanto um if-then-else não pode; por outro lado, um if-then-else pode executar loops e outras instruções, enquanto o operador ternário só pode executar (possivelmente anular) rvalues.

Em uma nota relacionada, o && e || operadores permitem alguns padrões de execução que são mais difíceis de implementar com if-then-else. Por exemplo, se alguém tem várias funções para chamar e deseja executar um trecho de código se algum deles falhar, isso pode ser feito de maneira adequada usando o operador &&. Fazê-lo sem esse operador exigirá código redundante, um goto ou uma variável de sinalizador extra.

Com o C # 7 , você pode usar o recurso new locals ref para simplificar a atribuição condicional de variables ​​compatíveis com ref. Então agora, não só você pode fazer:

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

… mas também o extremamente maravilhoso:

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

Essa linha de código atribui o valor de a para b ou c , dependendo do valor de i .


Notas
1. R-valor é o lado direito de uma atribuição, o valor que é atribuído.
2. l-value é o lado esquerdo de uma atribuição, a variável que recebe o valor atribuído.