Преимущества использования условного?: (Тройного) оператора

Каковы преимущества и недостатки оператора?: в отличие от стандартного оператора if-else. Очевидными являются:

Условный?: Оператор

  • Более короткий и более сжатый при сравнении с прямыми сопоставлениями значений и присваиванием
  • Кажется, он не такой гибкий, как конструкция if/else

Стандарт If/Else

  • Может применяться к большему количеству ситуаций (таких как вызовы функций)
  • Часто излишне длинный

Чтение, по-видимому, меняется для каждого в зависимости от оператора. Некоторое время спустя после первого контакта с оператором?: Мне потребовалось некоторое время, чтобы переварить, как это работает. Вы порекомендовали бы использовать его там, где это было возможно, или придерживаться if/else, учитывая, что я работаю со многими не-программистами?

Ответ 1

Я бы в принципе рекомендовал использовать его только тогда, когда результирующий оператор чрезвычайно короткий и представляет собой значительное увеличение лаконичности над эквивалентом if/else, не жертвуя удобочитаемостью.

Хороший пример:

int result = Check() ? 1 : 0;

Плохой пример:

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

Ответ 2

Это в значительной степени покрывается другими ответами, но "это выражение" на самом деле не объясняет, почему это так полезно...

В таких языках, как C++ и С#, вы можете определять локальные поля readonly (внутри тела метода), используя их. Это невозможно с помощью обычного оператора if/then, потому что значение поля readonly должно быть назначено внутри этого единственного оператора:

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

это не то же самое, что:

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

Аналогичным образом вы можете вставлять третичное выражение в другой код. Кроме того, чтобы сделать исходный код более компактным (и в некоторых случаях более читаемым в результате), он также может сделать сгенерированный машинный код более компактным и эффективным:

MoveCar((shiftKeyDown) ? 10 : 1);

... может генерировать меньше кода, чем повторять один и тот же метод дважды:

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

Конечно, это также более удобная и сжатая форма (меньше набрав, меньше повторений и может уменьшить вероятность ошибок, если вам придется дублировать куски кода в if/else). В чистых случаях "общего шаблона":

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

... просто быстрее читать/разбирать/понимать (как только вы привыкли к нему), чем длинный эквивалент if/else, поэтому он может быстрее ускорить процесс "grok".

Конечно, только потому, что это полезно, это не значит, что это лучшее, что нужно использовать в каждом случае. Я бы посоветовал использовать его только для коротких фрагментов кода, где смысл становится понятным (или более понятным) с помощью ?: - если вы используете его в более сложном коде или устанавливаете тройные операторы внутри друг друга, он может сделать код ужасно трудным читать.

Ответ 3

Я нахожу это особенно полезным при выполнении веб-разработки, если я хочу установить переменную в значение, отправленное в запросе, если оно определено или какое-то значение по умолчанию, если оно не является.

Ответ 4

Обычно я выбираю тернарный оператор, когда у меня будет много дублирующего кода.

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

С помощью тернарного оператора это может быть выполнено следующим образом.

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

Ответ 5

Действительно классное использование:

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

Ответ 6

Условный оператор отлично подходит для коротких условий, например:

varA = boolB ? valC : valD;

Я использую это время от времени, потому что для этого требуется меньше времени, чтобы написать что-то... К сожалению, это разветвление иногда может быть упущено другим разработчиком, просматривающим ваш код. Кроме того, код обычно не так короток, поэтому я обычно помогаю читабельности, помещая? и: в отдельных строках, например:

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

Однако большое преимущество использования блоков if/else (и почему я их предпочитаю) заключается в том, что он легче входить позже и добавлять дополнительную ветвь к ветке,

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

или добавьте другое условие:

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

Итак, в конце концов, это о удобстве для вас сейчас (короче использовать:?) по сравнению с удобством для вас (и других) позже. Это решение... но, как и все другие проблемы с форматированием кода, единственное реальное правило должно быть последовательным и быть визуально вежливым с теми, кто должен поддерживать (или оценивать!) Ваш код.

(весь скомпилированный код)

Ответ 7

Одна вещь, которую следует признать при использовании тернарного оператора, является выражением, а не выражением.

В функциональных языках, подобных схеме, различие не существует:

(если ( > a b) a b)

Условный?: Оператор "Кажется, что это не так гибко, как конструкция if/else"

В функциональных языках это.

При программировании на императивных языках я применяю тернарный оператор в ситуациях, когда я обычно использую выражения (назначение, условные операторы и т.д.).

Ответ 8

Несмотря на то, что вышеприведенные ответы действительны, и я согласен с важностью чтения, есть еще два момента:

  • В С# 6 вы можете использовать методы с выраженным выражением.

Это делает особенно кратким использование троичного:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. Поведение отличается, когда дело доходит до неявного преобразования типов.

Если у вас есть типы T1 и T2, которые могут быть неявно преобразованы в T, то ниже выполняется не:

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

(потому что компилятор пытается определить тип тройного выражения, и нет преобразования между T1 и T2.)

С другой стороны, версия if/else ниже работает:

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

потому что T1 преобразуется в T, и поэтому T2

Ответ 9

Иногда это может сделать назначение значения bool более легким для чтения с первого взгляда:

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

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

Ответ 10

Если я устанавливаю значение, и я знаю, что для него всегда будет одна строка кода, я обычно использую трехмерный (условный) оператор. Если есть шанс, что мой код и логика изменится в будущем, я использую if/else, поскольку это более понятно другим программистам.

Более интересным для вас может быть ? Оператор.

Ответ 11

Преимущество условного оператора состоит в том, что он является оператором. Другими словами, он возвращает значение. Поскольку if - это оператор, он не может вернуть значение.

Ответ 12

Я бы рекомендовал ограничить использование тернарного (?:) оператора простым назначением одной строки, если логика /else. Что-то похожее на этот шаблон:

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

Может быть легко преобразован в:

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

Я бы избегал использования тернарного оператора в ситуациях, которые требуют, если /else if/else, вложенные if/else или логика ветвления else else, что приводит к оценке нескольких строк. Применение тернарного оператора в этих ситуациях, скорее всего, приведет к нечитаемому, запутанному и неуправляемому коду. Надеюсь, это поможет.

Ответ 13

Есть ли какое-то преимущество в производительности от использования? оператор, например. MS Visual С++, но это действительно специфическая компилятор. В некоторых случаях компилятор может фактически оптимизировать условную ветвь.

Ответ 14

Сценарий, который я больше всего использую, используется для значений по умолчанию и особенно в результатах

return someIndex < maxIndex ? someIndex : maxIndex;

Это действительно единственные места, где я нахожу это приятным, но для них я это делаю.

Хотя, если вы ищете логическое значение, это может иногда выглядеть как подходящее:

bool hey = whatever < whatever_else ? true : false;

Потому что это так легко читать и понимать, но эта идея всегда должна быть брошена для более очевидной:

bool hey = (whatever < whatever_else);

Ответ 15

Если вам нужно несколько ветвей в одном и том же состоянии, используйте if:

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

Если вам нужно несколько ветвей с разными условиями, то если счетчик счетчиков будет иметь снежок, вы захотите использовать тройной:

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

Кроме того, вы можете использовать тернарный оператор при инициализации.

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

Выполнение этого с if очень грязно:

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

Вы не можете поместить инициализацию внутри if/else, потому что она меняет область. Но ссылки и константные переменные могут быть связаны только при инициализации.

Ответ 16

Тернарный оператор может быть включен в r-значение, тогда как if-then-else не может; с другой стороны, if-then-else может выполнять циклы и другие операторы, тогда как тернарный оператор может выполнять только (возможно, void) rvalues.

В соответствующем примечании, && и || операторы допускают некоторые шаблоны выполнения, которые сложнее реализовать с помощью if-then-else. Например, если у вас есть несколько функций для вызова и желание выполнить часть кода, если какой-либо из них не работает, это можно сделать красиво, используя && оператор. Выполнение этого без этого оператора потребует избыточного кода, перетаскивания или дополнительной переменной флага.

Ответ 17

С помощью С# 7 вы можете использовать новую функцию loc locals для упрощения условного назначения ref-совместимых переменных. Итак, теперь вы не только можете:

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

... но и чрезвычайно замечательно:

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

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

Эта строка кода присваивает значение a либо b либо c, в зависимости от значения i.



Заметки
1. r-значение - это правая часть присваивания, значение, которое присваивается.
2. l-значение - левая сторона присваивания, переменная, которая получает назначенное значение.