Переваги використання умовного?: (Потрійного) оператора


101

Які переваги та недоліки оператора ?: на відміну від стандартного оператора if-else. Очевидні:

Умовно?: Оператор

  • Коротше і більш стисло, якщо мати справу з прямим порівнянням цінностей та призначенням
  • Здається, це не так гнучко, як конструкція if / else

Стандартний If / Else

  • Можна застосувати до більшої кількості ситуацій (наприклад, викликів функцій)
  • Часто надмірно довгі

Здається, читабельність змінюється для кожного залежно від твердження. Дещо після того, як я вперше потрапив до оператора ?:, мені знадобилося певний час, щоб переконатися, як саме він працює. Чи рекомендуєте ви використовувати його там, де це можливо, або дотримуватися, якщо / ще з огляду на те, що я працюю з багатьма непрограмістами?


8
Ви вже отримали суть цього.
Байрон Вітлок

1
@Nicholas Knight: Я здогадуюсь, що OP означає, що ви не можете цього робити, наприклад, SomeCheck() ? DoFirstThing() : DoSecondThing();- ви повинні використовувати вираз, щоб повернути значення.
Дан Дао

6
Використовуйте його там, де зрозуміло , дотримуйтесь if / else, якщо це не так. Чіткість коду має стати вашим головним фактором.
hollsk

8
Ти бачив '??' ще? Серйозно, якщо ви вважаєте, що тернари круті ...
pdr

3
+1 за те, що не називаю це просто "потрійним оператором", як це роблять багато хто. Хоча це єдиний потрійний (на відміну від унарного та бінарного) оператора в C #, це не його назва.
Джон М Гант

Відповіді:


122

Я в основному рекомендую використовувати його лише тоді, коли отримане твердження є надзвичайно коротким і являє собою значне збільшення стислість над еквівалентом if / else без шкоди для читабельності.

Хороший приклад:

int result = Check() ? 1 : 0;

Поганий приклад:

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

5
Хороший дзвінок, але для запису, це "стислість".
mqp

6
@mquander, ти впевнений у цьому? merriam-webster.com/dictionary/concise
Байрон

39
Я завжди починаю з простого і роблю його складнішим з часом, поки він не стане абсолютно нечитабельним.
Джоке ван дер Маас

9
Читання у другому прикладі легко виправити з кращим форматуванням. Але, як рекомендує ОП, вона зводиться до читабельності та строгості проти багатослівності.
Натан Ернст

4
Не є частиною питання ОП, але важливо відзначити той факт, що ви не можете returnбути частиною результату потрійної операції. Наприклад: check() ? return 1 : return 0;не буде працювати, але return check() ? 1 : 0;буде. Завжди цікаво знаходити ці маленькі химерності в програмуванні.
CSS

50

Це в значній мірі охоплене іншими відповідями, але "це вираз" насправді не пояснює, чому це так корисно ...

Такими мовами, як C ++ та C #, ви можете визначати локальні поля для читання лише в межах методу, використовуючи їх. Це неможливо із звичайним оператором if / then, оскільки значення поля, яке читається тільки для цього, повинно бути присвоєне в цьому одному операторі:

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;

... це просто швидше читати / розбирати / розуміти (як тільки ви звикли до цього), ніж довговічне, якщо / ще еквівалент, тож це може допомогти вам швидше "виправити" код.

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


@JaminGrey "це не означає, що коли константа створюється, вона встановлюється на 10 або 1." Ви маєте на увазі, чи означає це? Неправильні коментарі можуть спричинити більше плутанини у нових програмістів на C ++, ніж проблема, яку ви намагалися усунути;)
Clonkex

5
Для майбутніх читачів, які стикаються з цим, " const int speed = (shiftKeyDown)? 10: 1; ", це означає, що коли константа вперше створюється , вона встановлюється на 10 або 1. Це не означає, що кожен раз доступ до константи він робить перевірку. (Тільки випадок заплутався новий програміст на C ++)
Джамін Грей

2
... або кажучи інакше, a constє постійним, тобто його неможливо змінити після того, як виконано оператор, у якому він оголошений.
Джейсон Вільямс

1
@JaminGrey. Чи не повинно це бути readonly? Я завжди думав, що constозначає " вирішено під час компіляції та вкладене там, де використовується ".
Нолонар

1
@ColinWiseman, це приклад для ілюстрації того, як?: Можна використовувати. Я спеціально заявляю, що лише тому, що ви можете це зробити, це не означає, що це обов'язково "найкраще", що потрібно зробити в будь-якому конкретному випадку. Щоб вирішити це, очікується, що читач використовуватиме їхній мозок щоразу, коли траплятиметься у випадку, коли це може бути їм корисним.
Джейсон Вільямс

14

Я зазвичай вибираю потрійного оператора, коли в іншому випадку у мене буде багато повторюваного коду.

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

12
особисто я б aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);особливо якщо b, c, dі eпотрібне лікування теж.
corsiKa

10
Навіщо взагалі використовувати умовне в цьому прикладі? Просто отримайте Abs (a) і зателефонуйте compute ().
Еш

2
Так, я не створив найкращого прикладу. :)
Ryan Bright

Новачку це не виглядає рівнозначно. Чи не потрібно було відповідати = обчислити (a> 0? A, b, c, d, e: -a, b, c, d, e); ?
pbreitenbach

@pbreitenbach: ні - це питання пріоритету - перший аргумент compute(...)є a > 0 ? a : -1, який оцінюється окремо від інших аргументів, розділених комами. У будь-якому випадку, на жаль, у C ++ не вистачає позначень, у яких постає ваше запитання для обробки "кортежів" значень, розділених комами, тому навіть a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)є незаконним, і немає нічого дуже подібного, що працює без змін на computeсобі.
Тоні Делрой

12

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


3
Значення за замовчуванням +1 у веб-розробниках є прекрасним прикладом хорошого місця для використання потрійного оператора
Байрон Вітлок

11

По-справжньому класним використанням є:

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

10
Будьте уважні з цим у PHP, потрійний оператор асоціює неправильний шлях у PHP. По суті, якщо fooце неправда, то вся справа буде оцінена до 4, не роблячи інших тестів.
Том Бусбі

4
@TomBusby - Нічого собі Ще одна причина ненавидіти PHP, якщо ви хтось, хто вже ненавидить PHP.
Тодд Леман

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

Отже, врешті-решт, про зручність для вас зараз (коротше використовувати:?) Проти зручності для вас (та інших) пізніше. Це виклик судження ... але, як і всі інші проблеми форматування коду, єдине реальне правило - бути послідовним і бути візуально ввічливим до тих, хто повинен підтримувати (або оцінювати!) Свій код.

(усі зібрані в коді)


5

Одне слід визнати при використанні потрійного оператора, що це вираз, а не вислів.

У функціональних мовах, як схема, розрізнення не існує:

(якщо (> ab) ab)

Умовно?: Оператор "Не здається таким гнучким, як конструкція if / else"

У функціональних мовах це.

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


5

Хоча вищезазначені відповіді справедливі, і я погоджуюся з важливістю читабельності, є ще два моменти:

  1. У C # 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


5

Іноді це може полегшити присвоєння значення bool на перший погляд:

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

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

4

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

Наступний інтерес для вас може бути ?? оператор .


4

Перевага умовного оператора полягає в тому, що він є оператором. Іншими словами, вона повертає значення. Оскільки ifце твердження, воно не може повернути значення.


4

Я рекомендую обмежити використання термінального (? :) оператора простим присвоєнням одного рядка, якщо / else логікою. Щось нагадує цей зразок:

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

Можна легко перетворити на:

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

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


2

Існує деяка перевага від продуктивності використання? оператор у напр. MS Visual C ++, але це дійсно конкретна річ у компіляторі. Компілятор може в деяких випадках оптимізувати умовну гілку.


2

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

return someIndex < maxIndex ? someIndex : maxIndex;

Це дійсно єдині місця, які мені здаються приємними, але для них я це роблю.

Хоча якщо ви шукаєте булевого, це іноді може виглядати як відповідна річ:

bool hey = whatever < whatever_else ? true : false;

Тому що це так легко читати і розуміти, але цю ідею завжди слід кидати для більш очевидного:

bool hey = (whatever < whatever_else);

2

Якщо вам потрібно кілька відділень в одній умові, використовуйте 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;

Зробити це з, якщо дуже безладно:

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

Ви не можете поставити ініціалізацію всередині if / else, оскільки це змінює область застосування. Але посилання та змінні const можуть бути зв'язані лише при ініціалізації.


2

Потрійний оператор може бути включений в оцінку, тоді як "if-then-else" не може; з іншого боку, if-then-else може виконувати цикли та інші оператори, тоді як потрійний оператор може виконувати лише (можливо, недійсні) rvalues.

У відповідній примітці && та || Оператори дозволяють деякі схеми виконання, які складніше реалізувати за допомогою if-then-else. Наприклад, якщо у вас є кілька функцій для виклику та бажає виконати фрагмент коду, якщо будь-яка з них виходить з ладу, це можна зробити добре за допомогою оператора &&. Якщо це зробити без цього оператора, знадобиться або зайвий код, goto, або додаткова змінна прапор.


1

За допомогою C # 7 ви можете використовувати нову функцію локальних посилань для спрощення умовного призначення змінних сумісних. Тож тепер ви не тільки можете:

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-значення - це ліва сторона призначення, змінна, яка отримує призначене значення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.