Преимущества использования условного?: (Тройного) оператора
Каковы преимущества и недостатки оператора?: в отличие от стандартного оператора 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";
- Поведение отличается, когда дело доходит до неявного преобразования типов.
Если у вас есть типы 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-значение - левая сторона присваивания, переменная, которая получает назначенное значение.