間違った質問をしている可能性があります。主に効率性(二次的な懸念事項かもしれませんが)のために、一方を他方の上に使用することを選択しませんが、実用性のためです。
目的が異なるため、実際には比較する必要??
があります。はい、それらはすべて何らかの形の「条件付き」の良さですが、重要なのは、両方が値に評価されるのに対し、そうではないため、多くの場合、異なる用途があります。?:
if
??
?:
if
たとえば、次のコードです。
Console.WriteLine("The order} is for {1} product",
orderId, productId ?? "every");
if
:で書くのは不格好でしょう
if (productId == null)
{
Console.WriteLine("The order {0} is for every product",
orderId);
}
else
{
Console.WriteLine("The order {0} is for {1} product",
orderId, productId);
}
はい、1つに凝縮することはできますが、一時変数などがあります。
if (productId == null)
{
productId = "every";
}
Console.WriteLine("The order {0} is for {1} product",
orderId, productId);
したがって、実際には、2つを比較するべきではありません。なぜなら??
、引数がである場合、のポイントは値に評価されることですがnull
、のポイントはif
別のパスを実行することです(直接値になりません)。
だから、より良い質問は、なぜ代わりにこれをしないのかということかもしれません:
Console.WriteLine("The order {0} is for {1} product",
orderId, productId == null ? "every" : productId);
これはほとんど同じであり(両方とも値に評価されます)、フロー制御にはそれほど多くありません。
それでは、違いを見てみましょう。このコードを3つの方法で書いてみましょう。
// Way 1 with if
string foo = null;
string folder = foo;
if (folder == null)
{
folder = "bar";
}
// Way 2 with ? :
string foo2 = null;
var folder2 = foo2 != null ? foo2 : "bar";
// Way 3 with ??
string foo3 = null;
var folder3 = foo3 ?? "bar";
IFの場合、次のILを取得します。
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldnull
IL_0005: ceq
IL_0007: ldc.i4.0
IL_0008: ceq
IL_000A: stloc.1
IL_000B: ldloc.1
IL_000C: brtrue.s IL_0016
IL_000E: nop
IL_000F: ldstr "bar"
IL_0014: stloc.0
条件付き(?:)の場合、次のILが得られます。
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: brtrue.s IL_000D
IL_0006: ldstr "bar"
IL_000B: br.s IL_000E
IL_000D: ldloc.0
IL_000E: nop
IL_000F: stloc.1
null合体(??)の場合、次のILを取得します。
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: dup
IL_0005: brtrue.s IL_000D
IL_0007: pop
IL_0008: ldstr "bar"
IL_000D: stloc.1
連続するものがどれほど単純であるかに注意してください。個別のif
ステートメントを処理するために分岐ロジックが必要なため、ILは大きくなります。?:
は(他のステートメントに分岐しない)値に評価されるだけで、()と比較するためにオペランドをロードする必要があるため、は小さくなりますnull
。
??
と比較するためのIL命令があるため、これはすべての中で最も単純です(null
vsロードnull
および比較)。
つまり、ILに関してはごくわずかな違いであり、パフォーマンスに影響する場合と影響しない場合があります。とにかく、プログラム(数学、データベース、ネットワークなど)でのより集中的な作業と比較して、これによる大きな違いはほとんどない可能性があります。
したがって、最も読みやすいものを選択し、プロファイリングを通じて現在の方法が不十分でボトルネックであることがわかった場合にのみ最適化することをお勧めします。
私にとって、?:
またはを使用する本当の理由??
は、最終結果を値にしたい場合です。つまり、あなたが書きたくなるときはいつでも:
if (someCondition)
x = value1;
else
x = value2;
次に、条件付き(?:
)を使用します。これは、それが優れた省略形であるためです。 x
この条件に基づいていずれかの値を取得します。
次に、さらに進んで、同じことが当てはまると言います。識別子の-nessに??
基づいて変数に値を割り当てたいと思います。null
したがってif
、フロー制御には最適ですが、2つの値のいずれかを返す場合、または条件に基づいて2つの値のいずれかを割り当てる場合は、?:
または必要に応じて使用??
します。
最後に、これらの機能(ILおよび関連するパフォーマンス)が.NET Frameworkのリビジョンごとに変更される可能性があることを覚えておいてください(私が今これを書いている時点では、それらはすべて無視できる程度です)。
したがって、今日は速くなるかもしれませんが、明日は速くないかもしれません。繰り返しになりますが、私はちょうど最もよく合うもので行くと言います、そしてあなたは最も読みやすいと思います。
アップデート
ちなみに、本当に夢中になっているので、上記の各コード見本の10,000,000回の反復を比較しました。これが、それぞれを実行するための合計時間です。??
私にとっては最速のように見えますが、やはりこれらはほとんど重要ではないほど近いです...
10,000,000 iterations of:
?: took: 489 ms, 4.89E-06 ms/item.
?? took: 458 ms, 4.58E-06 ms/item.
if took: 641 ms, 6.41E-06 ms/item.