これらの2行は同じですか?…: 'vs' ?? '?


80

これらの2つの線の間に違いはありますか?

MyName = (s.MyName == null) ? string.Empty : s.MyName

または

MyName = s.MyName ?? string.Empty

8
それらのILを確認してください。そうすれば、それらが同じコードを生成するのか、それとも異なる方法で処理されるのかが
わかり

12
@manman誰かが高レベルのコードを理解していない場合、ILコードがより読みやすくなるという印象を与えるものは何ですか?
Servy 2014年

11
@Servy違いを学ぶことに興味があり、他のすべての人が高レベルの議論をカバーしている場合は、違いを理解するための他の方法を指摘することをお勧めします。彼が非常に興味がある場合は、結果を比較するだけです。 IL全体を読む必要があります
manman 2014年

6
唯一の違いは、s.MyName1回評価するか2回評価するかです。
ティムS.

2
あなたはILコードを理解する必要がないため@Servy、あなただけに持っているdiffこと
wchargin

回答:


166

更新:私はこのトピックをより深く議論するブログ投稿を書きました。 http://www.codeducky.org/properties-fields-and-methods-oh-my/


通常、同じ結果が返されます。ただし、最初の例ではゲッターが2回実行され、2番目の例では1回しか実行されないMyNameため、プロパティの場合に顕著な違いが発生する場合がいくつかありますMyName

たとえば、MyName2回実行するとパフォーマンスが異なる場合があります。

string MyName
{
    get 
    {
        Thread.Sleep(10000);
        return "HELLO";
    }
}

または、ステートフルのMyName場合、2回実行すると異なる結果が得られる可能性がありますMyName

private bool _MyNameHasBeenRead = false;

string MyName
{
    get 
    {
        if(_MyNameHasBeenRead)
                throw new Exception("Can't read MyName twice");
        _MyNameHasBeenRead = true;
        Thread.Sleep(10000);
        return "HELLO";
    }
}

または、別のスレッドで変更できるMyName場合MyNameは、2回実行すると異なる結果が得られる可能性があります。

void ChangeMyNameAsync()
{
    //MyName set to null in another thread which makes it 
    //possible for the first example to return null
    Task.Run(() => this.MyName = null);
}

string MyName { get; set; }  

実際のコードのコンパイル方法は次のとおりです。最初に三元式の作品:

IL_0007:  ldloc.0     // s
IL_0008:  callvirt    s.get_MyName       <-- first call
IL_000D:  brfalse.s   IL_0017
IL_000F:  ldloc.0     // s
IL_0010:  callvirt    s.get_MyName       <-- second call
IL_0015:  br.s        IL_001C
IL_0017:  ldsfld      System.String.Empty
IL_001C:  call        set_MyName

これがnull合体演算子を使用した部分です。

IL_0007:  ldloc.0     // s
IL_0008:  callvirt    s.get_MyName       <-- only call
IL_000D:  dup         
IL_000E:  brtrue.s    IL_0016
IL_0010:  pop         
IL_0011:  ldsfld      System.String.Empty
IL_0016:  call        s.set_MyName

ご覧のとおり、三項演算子のコンパイル済みコードは、プロパティ値を取得するために2回の呼び出しを行いますが、null合体演算子は1回しか実行しません。


3
MyName = s.MyName ?? string.EmptyMyNameプロパティの場合、ゲッターを2回実行しません。したがって、ゲッターを2回実行したくない場合は、2行目を使用する必要があります。
Steven Wexler 2014年

2
私は、2つのコードのILの例を自由に追加しました。LINQPadの完全な例については、dropbox.com / s / x6zqdsjlkosxchf / SO21052437.linq
Lasse V. Karlsen

1
ILコードの最後の部分の「s」を見逃したと思います。現在は、と表示されet_MyNameますset_MyName
AJMansfield 2014年

2
また、の長いバージョンは、?:実際nullには上記の理由で発生する可能性があります。"Value from other thread"上記が実際にnull文字列である場合は、よりわかりやすくなる可能性があります。次に、結果nullになる可能性があることを示します。オブジェクトがgetアクセサの最初の呼び出しで文字列インスタンスを返し、2番目の呼び出しでのみnullを返すことを決定した場合、ステートフルオブジェクトでも同じことが起こる可能性があります。
Jeppe Stig Nielsen 2014

2
また、C#4.0言語仕様では(§7.14で)MyValueが実際に2回評価されるため、ジッターがその二重呼び出しを最適化するのを防ぐことができます(最初は思っていたように)
BlackBear 2014年

26

プロパティが単純なゲッター以上のものである場合、最初の関数のnull以外の場合に、関数を2回実行している可能性があります。

プロパティがステートフルオブジェクト内にある場合、プロパティへの2回目の呼び出しは異なる結果を返す可能性があります。

class MyClass
{
    private IEnumerator<string> _next = Next();

    public MyClass()
    {
        this._next.MoveNext();
    }

    public string MyName
    {
        get
        {
            var n = this._next.Current;
            this._next.MoveNext();
            return n;
        }
    }


    public static IEnumerator<string> Next()
    {
        yield return "foo";
        yield return "bar";
    }
}

また、文字列以外の場合、クラスは==をオーバーロードして、三項演算子とは異なる処理を実行する可能性があります。三項演算子が過負荷になる可能性があるとは思いません。


三項演算子は間違いなくオーバーロードできません。実質的に他のすべての演算子をオーバーロードできるC ++でさえ、オーバーロードする方法がありません?:。私はそうする言語を見たことがありません。
ダレルホフマン

@DarrelHoffman自分でロールできるScalaは常にあります;)
jdphenix 2014年

9

唯一の違いは、s.MyName2回評価するか1回評価するかです。1s.MyNameつ目はnullでない場合に2回実行し、2つ目は1回だけ評価します。

ほとんどの場合、この違いは重要ではありません。2番目の方がより明確で簡潔なので、2番目の違いを使用します。


5

はい、どちらも同じで、null合体演算子です。

オペランドがnullでない場合は、左側のオペランドを返します。それ以外の場合は、右側のオペランドを返します。

効率について話すなら、

string MyName = (s.MyName == null) ? string.Empty : s.MyName;
string MyName2 = s.MyName ?? string.Empty;

逆アセンブラーを使用すると、最初のステートメントではコンパイラーが19ステートメントを実行する必要があるのに対し、2番目のステートメントでは12ステートメントしか実行する必要がないことがわかります


名前を言ってくれてありがとう。Javaにもこれがあるかどうか見たかったのですか?? オペレーター。そうではないので、両方の三項演算子を使用します。
developerwjk

0

はい、彼らは同じことをします。??nullをチェックするための省略形です。


0

彼らは同じ仕事をします。

唯一の違いは、同僚またはコードを読んでいる人が構文を理解しているかどうかに関する読みやすさです。

編集:さらに、最初のオプションはプロパティをMyName2回評価できます。


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