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との比較に 「==
」オペレーターでの比較は行われない。(型に対して==
で独自定義した処理は呼ばれない)