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


277

C#7では、

if (x is null) return;

の代わりに

if (x == null) return;

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

セマンティクスは異なりますか?

好みの問題ですか?そうでない場合、いつどちらを使用すればよいですか?

参考:C#7.0の新機能


4
それは私が今見ていたリンクですが、それはあなたに多くの情報を提供しないので、OPが質問をしていると思います。ページの最も重要な部分はこのテストです。演算子 "is"演算子は、オブジェクトの実行時の型が特定の型と互換性があるかどうかを確認するために使用されます。つまり、「is」演算子を使用して、オブジェクトのタイプが想定どおりであることを確認します。その構文を見てみましょう:
Simon Price

2
@SimonPriceこれは、C#の現在のバージョンに関するものです。C#6。この質問は、パターンマッチングを備えたC#7に関するものです。
Patrick Hofman、2016年

@bigownどんな詳細をお探しですか?
Patrick Hofman、2016年

@PatrickHofman例としてスビックが答えた
Maniero

回答:


232

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

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


ためにnull、我々はC#で使っているものとの違いがないあなたが変更されたとき6.しかし、物事は面白いとなりnull、別の定数に。

これを例にとります:

Test(1);

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

テストの結果はaです。それをo == (object)1あなたが普通に書いたであろうものと比較すると、それは違いの地獄を作ります。is比較の反対側のタイプを考慮します。それはクールだ!

== nullvs. is null定数パターンは、「偶然」によく知られているものであり、is演算子と等号演算子の構文は同じ結果をもたらすと思います。


以下のようsvickはコメントし、is null呼び出しをSystem.Object::Equals(object, object)どこ==通話ceq

のIL is

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

私たちが話しているのでnull、これはインスタンスに違いをもたらすだけなので違いはありません。これは、等価演算子をオーバーロードしたときに変更される可能性があります。


16
@PatrickHofmanのようにコンパイルされている間、それはis呼び出しのように見えます。object.Equals(x, null)==ceqしかし、あなたが言ったように、結果は同じになるはずです。
2016年

17
==オーバーロード可能な演算子であることを常に注意してください。あなたはそれであなたが望む行動をすることができます。たとえば、この奇妙に実装され==ても、インスタンスが本当にnullであるかどうかはわかりません。is null一方、真のnull参照の場合は常にtrueを返します:)また、ReferenceEqualsコード内にある場合、VS 2017の電球は(正しく)is nullではなくに変更するように提案します== null
nawfal

2
@PatrickHofman @svick 2つのnullチェックが同じものにコンパイルされるようにisなったため、nullのチェックに使用したときに関数呼び出しのオーバーヘッドがなくなりました。証明については、コメントに@svickが投稿したリンクを参照してください。
AndreasHassing

1
@AndreasBjørnHassingNielsen私の回答を更新しました。
Patrick Hofman

2
@PatrickHofmanはILが反対であってはなりませんか?==はSystem.Object :: Equals(object、object)を呼び出し、nullはceqを呼び出します
ZbigniewLedwoń19年

68

オーバーロードされた等号演算子

演算子nullをオーバーロードした型と比較する場合、実際には2つの比較のセマンティクスに違いがあります==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);
    }

    // ...
}

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

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

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

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

したがって、違いは、使用する==とユーザーコードを実行するリスクがあることです(予期しない動作やパフォーマンスの問題が発生する可能性があります)。

ジェネリックの制限

構成を使用するとis null、タイプが参照タイプに制限されます。コンパイラはこれを保証します。つまりis null、値型では使用できません。ジェネリックメソッドがあるis null場合、ジェネリックタイプが参照タイプに制約されていない限り、使用できません。

bool IsNull<T>(T item) => item is null;                  // Compile error: CS0403
bool IsNull<T>(T item) => item == null;                  // Works
bool IsNull<T>(T item) where T : class => item is null;  // Works

これを指摘してくれたDavid Augusto Villaに感謝します。


2
さらに、xがジェネリック型の場合、(xはnull)にはクラス制約が必要ですが、(x == null)とobject.ReferenceEquals(x、null)には不要です。
デビッドアウグストヴィラ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.