LoginSignup
26
19

More than 5 years have passed since last update.

C#で「person?.Name」と「person == null ? null : person.Name」は等価じゃない。

Last updated at Posted at 2018-08-09

C#において

person?.Name」と「person == null ? null : person.Name」は等価じゃありません。

違う見方をすると、

null条件演算子 (?.) において、nullとの比較に 「==」オペレーターでの比較は行われません。(型に対して==で独自定義した処理は呼ばれない)

同様に、null合体演算子 (??)でも同じように、nullとの比較に 「==」オペレーターでの比較は行われません。(型に対して==で独自定義した処理は呼ばれない)

再現・確認コード

これを再現・確認できるコードは次の通りです。

// 本当はEqualsとかも実装するべき
class Person {
  public string Name { get; set; }

  public static bool operator ==(Person lhs, Person rhs) {
    Console.WriteLine("called ==");
    return object.ReferenceEquals(lhs, rhs);
  }

  public static bool operator !=(Person lhs, Person rhs) {
    Console.WriteLine("called !=");
    return !object.ReferenceEquals(lhs, rhs);
  }
}

void Main()
{
    Person personNonNull = new Person { Name = "Taro" };
    Console.WriteLine(personNonNull?.Name); 

    Person personNull = null;
    Console.WriteLine(personNull?.Name);
}

このコードを実行すると、「called ==」というログは表示されません。==オペレーターが呼ばれていないということが確認できますね。

ILの確認 ==を使った場合

void Main()
{
    Person person = new Person { Name = "Taro" };
    string name = person == null ? null : person.Name;
    Console.WriteLine();    
}

このコードのILは、こんな感じ。

IL_0000:  nop         
IL_0001:  newobj      UserQuery+Person..ctor
IL_0006:  dup         
IL_0007:  ldstr       "Taro"
IL_000C:  callvirt    UserQuery+Person.set_Name
IL_0011:  nop         
IL_0012:  stloc.0     // person
IL_0013:  ldloc.0     // person
IL_0014:  ldnull      
IL_0015:  call        UserQuery+Person.op_Equality
IL_001A:  brtrue.s    IL_0024
IL_001C:  ldloc.0     // person
IL_001D:  callvirt    UserQuery+Person.get_Name
IL_0022:  br.s        IL_0025
IL_0024:  ldnull      
IL_0025:  stloc.1     // name
IL_0026:  call        System.Console.WriteLine
IL_002B:  nop         
IL_002C:  ret
IL_0015:  call        UserQuery+Person.op_Equality

に注目してください。 ==の実装が呼ばれていますね。


ILの確認 null条件演算子を使った場合

void Main()
{
    Person person = new Person { Name = "Taro" };
    string name = person?.Name;
    Console.WriteLine();    
}

このコードのILは、こんな感じ。

IL_0000:  nop         
IL_0001:  newobj      UserQuery+Person..ctor
IL_0006:  dup         
IL_0007:  ldstr       "Taro"
IL_000C:  callvirt    UserQuery+Person.set_Name
IL_0011:  nop         
IL_0012:  stloc.0     // person
IL_0013:  ldloc.0     // person
IL_0014:  brtrue.s    IL_0019
IL_0016:  ldnull      
IL_0017:  br.s        IL_001F
IL_0019:  ldloc.0     // person
IL_001A:  call        UserQuery+Person.get_Name
IL_001F:  stloc.1     // name
IL_0020:  call        System.Console.WriteLine
IL_0025:  nop         
IL_0026:  ret

UserQuery+Person.op_Equalityが存在しないことに注目してください。==で定義したした処理は呼ばれないことに注目してください。

まとめ

person?.Name」と「person == null ? null : person.Name」は等価じゃありません。

違う見方をすると、

null条件演算子 (?.) において、nullとの比較に 「==」オペレーターでの比較は行われない。(型に対して==で独自定義した処理は呼ばれない)

同様に、null合体演算子 (??)でも同じように、nullとの比較に 「==」オペレーターでの比較は行われない。(型に対して==で独自定義した処理は呼ばれない)

こちらもどうぞ

関連記事 : Reshaper/Riderの「Possible unintended bypass of lifetime check of underlying Unity engine object」って何ぞや?

26
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
19