Sự khác biệt giữa các x x là null và xx = null null là gì?


277

Trong C # 7 chúng ta có thể sử dụng

if (x is null) return;

thay vì

if (x == null) return;

Có bất kỳ lợi thế nào khi sử dụng cách mới (ví dụ cũ) so với cách cũ không?

Các ngữ nghĩa có khác nhau không?

Chỉ là một vấn đề của hương vị? Nếu không, khi nào tôi nên sử dụng cái này hơn cái kia?

Tham khảo: Có gì mới trong C # 7.0 .


4
đó là liên kết tôi vừa xem, tuy nhiên nó không cung cấp cho bạn nhiều thông tin, đó là lý do tại sao tôi đoán OP đang đặt câu hỏi. Phần quan trọng nhất của trang là kiểm tra này là Toán tử Toán tử "is" được sử dụng để kiểm tra xem loại thời gian chạy của một đối tượng có tương thích với một loại nhất định hay không. Nói cách khác, chúng tôi sử dụng toán tử "is" để xác minh rằng loại đối tượng là những gì chúng tôi mong đợi. Hãy nhìn vào cú pháp của nó:
Simon Giá

2
@Simonprice Đó là về phiên bản hiện tại của C #: C # 6. Câu hỏi này là về C # 7, có khớp mẫu .
Patrick Hofman

@bigown bạn đang tìm loại chi tiết nào?
Patrick Hofman

@PatrickHofman loại Svick đã trả lời, ví dụ
Maniero

Câu trả lời:


231

Cập nhật: Trình biên dịch Roslyn đã được cập nhật để làm cho hành vi của hai toán tử giống nhau khi không có toán tử đẳng thức quá tải . Vui lòng xem mã trong kết quả trình biên dịch hiện tại ( M1M2trong mã) để hiển thị những gì xảy ra khi không có bộ so sánh đẳng thức quá tải. Cả hai đều có ==hành vi thực hiện tốt hơn . Nếu có một bộ so sánh đẳng thức quá tải, mã vẫn khác .

Xem các phiên bản cũ hơn của trình biên dịch Roslyn phân tích dưới đây.


nullkhông có sự khác biệt với những gì chúng ta đã quen với C # 6. Tuy nhiên, mọi thứ trở nên thú vị khi bạn thay đổi nullsang một hằng số khác.

Lấy ví dụ này:

Test(1);

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

Các thử nghiệm mang lại a. Nếu bạn so sánh điều đó với o == (object)1những gì bạn sẽ viết bình thường, nó sẽ tạo ra một sự khác biệt. isxem xét các loại ở phía bên kia của so sánh. Thật là tuyệt!

Tôi nghĩ rằng mô hình == nullso với is nullhằng số chỉ là một cái gì đó rất quen thuộc 'tình cờ', trong đó cú pháp của istoán tử và toán tử bằng cho kết quả giống nhau.


Như Svick bình luận, is nullgọi System.Object::Equals(object, object)nơi ==gọiceq .

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

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

Vì chúng ta đang nói về null, không có sự khác biệt vì điều này chỉ tạo ra sự khác biệt trong các trường hợp . Điều này có thể thay đổi khi bạn đã quá tải toán tử đẳng thức.


15
@PatrickHofman Trông giống như iscác cuộc gọi object.Equals(x, null), trong khi ==biên dịch thành ceq. Nhưng kết quả nên giống nhau, như bạn đã nói.
Svick

16
Luôn luôn cẩn thận trong tâm trí đó ==là một nhà điều hành quá tải. Bạn có thể có bất kỳ hành vi nào bạn muốn với nó. Ví dụ, việc triển khai kỳ lạ== này sẽ không cho bạn biết nếu trường hợp của bạn thực sự là null. is nullmặt khác sẽ luôn trả về true cho các tham chiếu null thực sự :) Ngoài ra, nếu bạn có ReferenceEqualstrong mã của mình, bóng đèn VS 2017 sẽ đề nghị thay đổi thành is null, không == null(chính xác).
nawfal

2
@PatrickHofman @svick hai kiểm tra null bây giờ được biên dịch thành cùng một thứ, do đó iskhông còn có chi phí của một lệnh gọi hàm khi được sử dụng để kiểm tra null. Để chứng minh, hãy xem liên kết được đăng bởi @svick trong phần bình luận.
AndreasHassing

1
@ AndreasBjørnHassingNielsen Cập nhật câu trả lời của tôi.
Patrick Hofman

2
@PatrickHofman không nên IL theo cách khác? == gọi System.Object :: Equals (object, object) và null gọi ceq
Zbigniew Ledwoń

67

Quá tải toán tử bằng

Thực tế, có một sự khác biệt về ngữ nghĩa giữa hai so sánh khi bạn so sánh nullvới một loại đã quá tải ==toán tử. foo is nullsẽ sử dụng so sánh tham chiếu trực tiếp để xác định kết quả, trong khi đó foo == nulltất nhiên sẽ chạy ==toán tử quá tải nếu nó tồn tại.

Trong ví dụ này, tôi đã giới thiệu một "lỗi" trong ==toán tử bị quá tải , khiến nó luôn luôn ném một ngoại lệ nếu đối số thứ hai là 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);
    }

    // ...
}

Mã IL để foo is nullsử dụng ceqhướng dẫn để thực hiện so sánh tham chiếu trực tiếp:

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

Mã IL để foo == nullsử dụng lệnh gọi đến toán tử quá tải:

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

Vì vậy, sự khác biệt là, nếu bạn sử dụng, ==bạn có nguy cơ chạy mã người dùng (có thể có khả năng có vấn đề về hành vi hoặc hiệu suất không mong muốn).

Hạn chế về thuốc generic

Sử dụng is nullcấu trúc giới hạn kiểu cho kiểu tham chiếu. Trình biên dịch đảm bảo điều này, có nghĩa là bạn không thể sử dụng is nulltrên một loại giá trị. Nếu bạn có một phương thức chung, bạn sẽ không thể sử dụng is nulltrừ khi loại chung bị ràng buộc là loại tham chiếu.

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

Cảm ơn David Augusto Villa đã chỉ ra điều này.


2
Ngoài ra, ghi chú (x là null) yêu cầu ràng buộc lớp nếu x là loại chung, trong khi (x == null) và object.ReferenceEquals (x, null) thì không.
Biệt thự David Augusto
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.