web-dev-qa-db-ja.com

"x is null"と "x == null"の違いは何ですか?

C#7では使うことができます

if (x is null) return;

の代わりに

if (x == null) return;

古い方法よりも新しい方法(前の例)を使用することに利点はありますか?

セマンティクスは違いますか?

単なる好みの問題ですか?そうでない場合、いつ私は一方を他方を使用すべきですか?

参照: C#7.0の新機能

177
Maniero

更新:オーバーロードされた等価演算子がない場合、Roslynコンパイラは2つの演算子の動作を同じにするように更新されましたオーバーロードされた等価比較子がない場合に何が起こるかを示す 現在のコンパイラ結果のコード (コードのM1M2)を参照してください。どちらも現在、パフォーマンスの良い==の動作をしています。オーバーロードされた等値比較子がある場合は、 コードは異なります

Roslynコンパイラの古いバージョンについては、以下の分析を参照してください。


nullname__については、C#6で慣れ親しんだものと違いはありません。しかし、nullname__を別の定数に変更すると、面白いことが起こります。

例を挙げましょう。

Test(1);

public void Test(object o)
{
    if (o is 1) Console.WriteLine("a");
    else Console.WriteLine("b");
}

テストの結果はaname__です。それをo == (object)1とあなたが普通に書いたであろうものと比較すれば、それは違いの地獄になります。 isname__は、比較の反対側の型を考慮に入れます。それはクールだ!

== nullis nullの定数パターンは単なる偶然の一致であり、isname__演算子とequals演算子の構文は同じ結果になります。


svick がコメントしたように、 is nullSystem.Object::Equals(object, object)を呼び出します。ここで==ceqname __を呼び出します

isname __のIL:

IL_0000: ldarg.1              // Load argument 1 onto the stack
IL_0001: ldnull               // Push a null reference on the stack
IL_0002: call bool [mscorlib]System.Object::Equals(object, object) // Call method indicated on the stack with arguments
IL_0007: ret                  // Return from method, possibly with a value

==のIL:

IL_0000: ldarg.1              // Load argument 1 onto the stack
IL_0001: ldnull               // Push a null reference on the stack
IL_0002: ceq                  // Push 1 (of type int32) if value1 equals value2, else Push 0
IL_0004: ret                  // Return from method, possibly with a value

nullname__について話しているので、これ以降は違いはありません インスタンス上でのみ違いが出ます 。等価演算子をオーバーロードすると、これは変わる可能性があります。

150
Patrick Hofman

実際には2つの比較の間に意味論の違いがあります。 null==演算子をオーバーロードした型と比較しているときに、Edgeケースが現れます。

foo is nullは直接参照比較を使用して結果を判断しますが、foo == nullはもちろんオーバーロードされた==演算子が存在する場合はそれを実行します。

この例では、私はオーバーロードされた==演算子に "バグ"を導入しました。それは、2番目の引数がnullである場合、常に例外を投げます。

void Main()
{
    Foo foo = null;

    if (foo is null) Console.WriteLine("foo is null"); // This condition is met
    if (foo == null) Console.WriteLine("foo == null"); // This will throw an exception
}

public class Foo
{
    public static bool operator ==(Foo foo1, Foo foo2)
    {
        if (object.Equals(foo2, null)) throw new Exception("oops");
        return object.Equals(foo1, foo2);
    }

    // ...
}

foo is nullのILコードはceq命令を使用して直接参照比較を実行します。

IL_0003:  ldloc.0     // foo
IL_0004:  ldnull      
IL_0005:  ceq

foo == nullのILコードは、オーバーロードされた演算子への呼び出しを使用します。

IL_0016:  ldloc.0     // foo
IL_0017:  ldnull      
IL_0018:  call        UserQuery+Foo.op_Equality

つまり、==を使用すると、ユーザーコードを実行する危険性があるという点です(予期しない動作やパフォーマンスの問題が発生する可能性があります)。

43