Apa perbedaan antara “x is null” dan “x == null”?

277

Di C # 7 kita bisa menggunakan

if (x is null) return;

dari pada

if (x == null) return;

Apakah ada keuntungan menggunakan cara baru (contoh sebelumnya) dibandingkan cara lama?

Apakah semantiknya berbeda?

Apakah hanya masalah selera? Jika tidak, kapan saya harus menggunakan yang satu?

Referensi: Apa yang Baru di C # 7.0 .

Maniero
sumber
4
itulah tautan yang baru saja saya lihat, tetapi itu tidak memberi Anda banyak informasi yang mengapa saya kira OP mengajukan pertanyaan. Bagian terpenting dari halaman ini adalah tes ini adalah Operator Operator "adalah" digunakan untuk memeriksa apakah tipe run-time suatu objek kompatibel dengan tipe yang diberikan atau tidak. Dengan kata lain, kami menggunakan operator "adalah" untuk memverifikasi bahwa jenis objek adalah seperti yang kami harapkan. Mari kita lihat sintaksnya:
Simon Price
2
@SimonPrice Itu tentang versi C #: C # 6. Pertanyaan ini adalah tentang C # 7, yang memiliki pola yang cocok .
Patrick Hofman
@bigown detail seperti apa yang Anda cari?
Patrick Hofman
@ Patrick Snofman, jenis svick yang menjawab, dengan contoh
Maniero

Jawaban:

232

Pembaruan: Kompilator Roslyn telah diperbarui untuk membuat perilaku kedua operator sama ketika tidak ada operator kesetaraan yang kelebihan beban . Silakan lihat kode dalam hasil kompiler saat ini ( M1dan M2dalam kode) yang menunjukkan apa yang terjadi ketika tidak ada pembanding kesetaraan kelebihan beban. Mereka berdua sekarang memiliki ==perilaku yang lebih baik . Jika ada pembanding kesetaraan kelebihan beban, kode masih berbeda .

Lihat untuk versi yang lebih lama dari kompiler Roslyn analisis di bawah ini.


Karena nulltidak ada perbedaan dengan apa yang biasa kita gunakan dengan C # 6. Namun, hal-hal menjadi menarik ketika Anda berubah nullke konstanta lain.

Ambil ini sebagai contoh:

Test(1);

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

Tes menghasilkan a. Jika Anda membandingkannya dengan o == (object)1apa yang Anda tulis secara normal, itu membuat perbedaan besar. ismempertimbangkan jenis di sisi lain perbandingan. Itu keren!

Saya pikir pola == nullvs is nullkonstan adalah sesuatu yang sangat akrab dengan 'kebetulan', di mana sintaksis isoperator dan operator sederajat menghasilkan hasil yang sama.


Seperti svick berkomentar, is nullpanggilan System.Object::Equals(object, object)tempat ==panggilanceq .

IL untuk 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 untuk ==:

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

Karena kita berbicara tentang null, tidak ada perbedaan karena ini hanya membuat perbedaan pada instance . Ini bisa berubah ketika Anda telah kelebihan beban operator kesetaraan.

Patrick Hofman
sumber
16
@ PatrickHofman Sepertinya ispanggilan object.Equals(x, null), sementara ==dikompilasi sebagai ceq. Tetapi hasilnya harus sama, seperti yang Anda katakan.
svick
17
Berhati-hatilah selalu bahwa itu ==adalah operator yang kelebihan beban. Anda dapat memiliki perilaku yang Anda inginkan dengannya. Untuk misalnya ini diterapkan aneh== tidak akan memberi tahu Anda jika instance Anda benar-benar nol. is nulldi sisi lain akan selalu mengembalikan true untuk referensi nol sejati :) Juga, jika Anda memiliki ReferenceEqualsdalam kode Anda, bola lampu VS 2017 akan menyarankan untuk berubah menjadi is null, bukan == null(dengan benar).
nawfal
2
@PatrickHofman @svick dua cek null sekarang mengkompilasi ke hal yang sama, sehingga istidak lagi memiliki overhead panggilan fungsi ketika digunakan untuk memeriksa null. Sebagai buktinya, lihat tautan yang diposting oleh @svick di komentar.
AndreasHassing
1
@ AndreasBjørnHassingNielsen Memperbarui jawaban saya.
Patrick Hofman
2
@ PatrickHofman seharusnya IL tidak menjadi sebaliknya? == Panggilan System.Object :: Sama dengan (objek, objek) dan null panggilan ceq
Zbigniew Ledwoń
68

Kelebihan muatan sama dengan operator

Sebenarnya ada perbedaan dalam semantik antara dua perbandingan ketika Anda membandingkan nulldengan jenis yang telah membebani ==operator. foo is nullakan menggunakan perbandingan referensi langsung untuk menentukan hasilnya, sedangkan foo == nulltentu saja akan menjalankan ==operator yang kelebihan beban jika ada.

Dalam contoh ini saya telah memperkenalkan "bug" pada ==operator yang kelebihan beban , menyebabkannya selalu mengeluarkan pengecualian jika argumen kedua adalah 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);
    }

    // ...
}

Kode IL untuk foo is nullmenggunakan ceqinstruksi untuk melakukan perbandingan referensi langsung:

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

Kode IL untuk foo == nullmenggunakan panggilan ke operator yang kelebihan beban:

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

Jadi perbedaannya adalah, bahwa jika Anda menggunakan ==Anda berisiko menjalankan kode pengguna (yang berpotensi memiliki perilaku yang tidak terduga atau masalah kinerja).

Pembatasan obat generik

Menggunakan is nullkonstruk membatasi tipe ke tipe referensi. Kompiler memastikan hal ini, yang berarti Anda tidak dapat menggunakan is nulltipe nilai. Jika Anda memiliki metode generik, Anda tidak akan dapat menggunakan is nullkecuali tipe generik dibatasi menjadi tipe referensi.

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

Terima kasih kepada David Augusto Villa yang telah menunjukkan ini.

Thorkil Holm-Jacobsen
sumber
2
Selain itu, note (x adalah nol) membutuhkan batasan kelas jika x adalah tipe generik, sedangkan (x == null) dan objek.ReferensiEquals (x, nol) tidak.
David Augusto Villa