1. ホーム
  2. c#

[解決済み] この2行は同じものなのでしょうか?

2023-07-06 22:43:03

質問

この2つの行に違いはあるのでしょうか?

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

または

MyName = s.MyName ?? string.Empty

どのように解決するのですか?

UPDATE: このトピックについて、より深く掘り下げたブログ記事を書きました。 http://www.codeducky.org/properties-fields-and-methods-oh-my/


一般に、これらは同じ結果を返します。 しかし、以下のような場合、顕著な違いを経験することになります。 MyName がプロパティであるため MyName ゲッターは最初の例では2回実行され、2番目の例では1回だけ実行されるからです。

を実行することによるパフォーマンスの違いを経験するかもしれません。 MyName を2回実行することによるパフォーマンスの違いを感じるかもしれません。

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

を実行すると、異なる結果が得られるかもしれません。 MyName を二回実行すると MyName がステートフルである場合、2回になります。

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 は別のスレッドで変更することができます。

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

実際のコードをコンパイルしてみると、以下のようになります。まず、3項式の部分です。

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

で、これがヌルコアレシング演算子を使った作品です。

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回の呼び出しを行いますが、ヌルコアレスティング演算子は1回しか行いません。