条件付き?:(3項)演算子を使用する利点


101

標準のif-elseステートメントとは対照的に、?:演算子の利点と欠点は何ですか。明白なものは:

条件付き?:演算子

  • 直接的な値の比較と割り当てを処理する場合、より簡潔で簡潔
  • if / elseコンストラクトほど柔軟ではないようです

標準のIf / Else

  • より多くの状況に適用できます(関数呼び出しなど)
  • 多くの場合、不必要に長い

読みやすさは、ステートメントによってそれぞれ異なります。最初に?:オペレーターに公開されてからしばらくの間、それがどのように機能するかを正確に説明するのに少し時間がかかりました。可能な限りそれを使用することをお勧めしますか、それとも私が多くの非プログラマーと作業しているのであれば、if / elseに固執することをお勧めしますか?


8
あなたはすでにその要点を理解しています。
バイロンウィットロック

1
@Nicholas Knight:OPは、たとえば、実行できないことを意味していると思いますSomeCheck() ? DoFirstThing() : DoSecondThing();-値を返すには式を使用する必要があります。
Dan Tao

6
明確な場所で使用し、明確でない場合はif / elseを使用してください。コードの明快さはあなたの主な考慮事項であるべきです。
ホリスク

8
「??」を見たことがありますか まだ?真剣に、もしあなたが三元系がクールだと思うなら...
pdr

3
+1は、多くの場合のように、単に「3項演算子」とは呼ばれません。これはC#の唯一の(単項および二項ではなく)三項演算子ですが、その名前ではありません。
ジョンMガント2010

回答:


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
Byron Whitlock、

39
私はいつもシンプルなものから始めて、完全に読めなくなるまで時間をかけて複雑にします。
Jouke van der Maas、2010

9
2番目の例の読みやすさは、書式設定を改善することで簡単に修正できます。しかし、OPが推奨しているように、それは読みやすさと簡潔さ対冗長性に帰着します。
Nathan Ernst

4
OPの質問の一部ではありませんreturnが、3項演算の結果の一部になることはできないという事実に注意することが重要です。例:機能し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);

...同じメソッドを2回呼び出すよりも少ないコードを生成する場合があります。

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

もちろん、これはより便利で簡潔な形式でもあります(入力が少なく、繰り返しが少なく、if / elseでコードのチャンクを複製する必要がある場合にエラーの可能性を減らすことができます)。このようなきれいな「一般的なパターン」の場合:

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

...(慣れれば)読み取り/解析/理解するのは、長い間使用されているif / elseの同等のものよりも単純に高速なので、コードをより速く「グロック」するのに役立ちます。

もちろん、それが役立つからといって、それがすべての場合に使用するのに最適なものであるとは限りません。使用することで意味が明確な(またはより明確になった)短いコードにのみ使用することをお勧めし?:ます-より複雑なコードで使用したり、三項演算子を相互にネストしたりすると、コードがひどく読みにくくなる可能性があります。


@JaminGrey 「これは、定数が作成されるときに、定数が10または1に設定されることを意味しません。」それどういう意味ですか?コメントが間違っていると、解決しようとしていた問題よりも、新しいC ++プログラマーに混乱をもたらす可能性があります;)
Clonkex

5
この横切って来る将来の読者のために、による「; CONST INT速度=(shiftKeyDown)10 1? 、一定の場合を意味する」最初に作成され、それは10または1に設定されているが、それはしないその都度平均定数はアクセスされ、チェックを行います。(ジャストは、新しいC ++プログラマが混乱していた包みなさい)
ヤミングレー

2
...または別の言い方をするconstと、a は定数です。つまり、宣言されているステートメントが実行された後は変更できません。
Jason Williams

1
@JaminGrey。それはそうではreadonlyないですか?私はいつもconstコンパイル時に解決され、使用される場所にインライン化される」という意味だと思っていました。
Nolonar 14

1
@ColinWiseman、それはどのように?:使用できるかを示すです。私は、あなたがそれを実行できるからといって、それが特定の場合に必ずしも「最良の」ことであるとは限らないと述べています。それを解決するために、読者は、それが彼らにとって役立つかもしれない事件に出くわすたびに彼らの脳を使うことが期待されています。
Jason Williams

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);場合は特にbcdeあまりにも治療が必要でした。
corsiKa 2010

10
この例でなぜ条件付きを使用するのですか?Abs(a)を取得して、compute()を1回呼び出すだけです。
アッシュ

2
ええ、私は最良の例を作成しませんでした。:)
ライアン・ブライト

初心者にとって、それは同等に見えません。答えである必要はないでしょうか= compute(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それ自体を変更せずに機能する非常に類似したものはありません。
Tony Delroy、2013年

12

リクエストで送信された値が定義されている場合は変数に設定し、そうでない場合はデフォルト値に変数を設定する場合、Web開発を行うときに特に役立ちます。


3
ウェブ開発でのデフォルト値の+1は、三項演算子を使用する良い例です
Byron Whitlock

11

本当にクールな使い方は:

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

10
PHPではこれに注意してください。3項演算子は、PHPでは間違った方法を関連付けます。基本的に、fooがfalseの場合、他のテストを実行せずに全体が4に評価されます。
トム・バズビー

4
@TomBusby —うわー。PHPを憎むもう1つの理由は、あなたがすでに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

三項演算子を使用するときに、それがステートメントではなく式であることを認識する1つのこと。

スキームのような関数型言語では、区別は存在しません:

(if(> ab)ab)

条件?:演算子「if / else構成ほど柔軟ではないようです」

関数型言語ではそうです。

命令型言語でプログラミングするときは、通常は式(代入、条件ステートメントなど)を使用する状況で三項演算子を適用します。


5

上記の回答は有効であり、読みやすさが重要であることには同意しますが、さらに考慮すべき点が2つあります。

  1. C#6では、式をボディとしたメソッドを使用できます。

これにより、3項の使用が特に簡潔になります。

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. 暗黙的な型変換に関しては、動作が異なります。

タイプがT1ありT2、どちらも暗黙的にに変換できるT場合、以下は機能しませ

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

(コンパイラーは3値式のタイプを判別しようとするため、T1T2)。

一方、if/else以下のバージョンは機能します。

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

T1変換されるTのでT2


5

一見すると、ブール値の割り当てが読みやすくなる場合があります。

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

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

4

値を設定していて、常に1行のコードであることがわかっている場合は、通常、3項(条件付き)演算子を使用します。私のコードとロジックが将来変更される可能性がある場合は、他のプログラマーにとってより明確であるため、if / elseを使用します。

さらに興味があるのは?? 演算子


4

条件演算子の利点は、それが演算子であることです。つまり、値を返します。以来if文があり、それが値を返すことはできません。


4

ternary(?:)演算子の使用を単純な単一行の割り当てif / elseロジックに制限することをお勧めします。このパターンに似たもの:

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

簡単に次のように変換できます:

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

if / else if / 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);

条件が異なる複数のブランチが必要な場合、ステートメントカウントが雪だるま式になる場合は、3項を使用する必要があります。

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内に置くことはできません。ただし、参照とconst変数は、初期化時にのみバインドできます。


2

三項演算子は右辺値に含めることができますが、if-then-elseは含めることができません。一方、if-then-elseはループやその他のステートメントを実行できますが、3項演算子は右辺値のみ(おそらくvoid)しか実行できません。

関連する注記として、&&と|| 演算子は、if-then-elseで実装するのが難しいいくつかの実行パターンを許可します。たとえば、呼び出す関数がいくつかあり、それらのいずれかが失敗した場合にコードの一部を実行したい場合、&&演算子を使用して適切に実行できます。その演算子なしでそれを行うには、冗長なコード、goto、または追加のフラグ変数のいずれかが必要になります。


1

C#7には、新しい使用することができREFの地元の人々は、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値がある割り当ての-hand側、割り当てられた値を受け取る変数。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.