7

使用しますか?? null 値に対して式を評価する演算子。例:

string foo = null;
string bar = "woooo";
string foobar= foo ?? bar ; 
// Evaluates foobar as woooo

if上記の式で使用した場合と同じように機能するステートメントも使用しました

string foo = null;
string bar = "woooo";
if(foo==null)
   string foobar=   "woooo" ;
// Evaluates foobar as woooo same as above

また?: 三項演算子...

string foo = null;
string bar = "woooo";    
string foobar= foo==null ? "woooo" : null ;
// Evaluates foobar as woooo same as above

null合体は構文が正確であることは知っていますが、両方の中でどちらがより速くコンパイルされ、より速く実行されますか?またその理由は?

4

2 に答える 2

23

間違った質問をしている可能性があります。主に効率性(二次的な懸念事項かもしれませんが)のために、一方を他方の上に使用することを選択しませんが、実用性のためです。

目的が異なるため、実際には比較する必要??があります。はい、それらはすべて何らかの形の「条件付き」の良さですが、重要なのは、両方が値に評価されるのに対し、そうではないため、多くの場合、異なる用途があります。?: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命令があるため、これはすべての中で最も単純です(nullvsロード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.
于 2012-11-14T19:15:25.977 に答える
5

両方とも同じILにコンパイルする必要があるため、同じように実行する必要があります。

そうしなかったとしても、パフォーマンスの違いはごくわずかです。アプリケーションのプロファイルを作成し、ステートメントにかなりの時間がかかるのを見ない限り、これを見ることすら考えません。

以前ではなく、パフォーマンスの問題を引き起こしていることが証明できない限り、常に最も明確な構文を使用してください。

于 2012-11-14T19:03:52.757 に答える