Tại sao người ta thường thấy “null! = Biến” thay vì “biến! = Null” trong C #?


103

Trong c #, có sự khác biệt nào về tốc độ xử lý đối với thứ tự mà bạn nêu điều kiện không?

if (null != variable) ...
if (variable != null) ...

Kể từ gần đây, tôi nhìn thấy cái đầu tiên khá thường xuyên, và nó khiến tôi chú ý vì tôi đã quen với cái thứ hai.

Nếu không có sự khác biệt, lợi thế của cái đầu tiên là gì?



Một số ngôn ngữ lập trình hỗ trợ khởi tạo giá trị trong điều kiện if. Trong các ngôn ngữ như vậy, nếu chúng ta muốn so sánh LValue với RValue, bạn nên sử dụng từ sang trái như if (1 == i). do đó, vô tình nếu chúng ta đặt = thay vì == thì nó gây ra lỗi trình biên dịch.
Jugal Panchal

Câu trả lời:


160

Đó là sự cố giữ lại từ C. Trong C, nếu bạn sử dụng một trình biên dịch kém hoặc không có cảnh báo đủ cao, điều này sẽ biên dịch mà không có bất kỳ cảnh báo nào (và thực sự là mã hợp pháp):

// Probably wrong
if (x = 5)

khi bạn thực sự có thể có ý nghĩa

if (x == 5)

Bạn có thể giải quyết vấn đề này trong C bằng cách:

if (5 == x)

Một lỗi đánh máy ở đây sẽ dẫn đến mã không hợp lệ.

Bây giờ, trong C #, đây là tất cả những gì phức tạp. Trừ khi bạn đang so sánh hai giá trị Boolean (hiếm gặp, IME), bạn có thể viết mã dễ đọc hơn, vì câu lệnh "if" yêu cầu bắt đầu bằng biểu thức Boolean và loại " x=5" Int32thì không Boolean.

Tôi đề nghị rằng nếu bạn thấy điều này trong mã của đồng nghiệp của mình, bạn nên giáo dục họ theo cách của các ngôn ngữ hiện đại và đề nghị họ viết dạng tự nhiên hơn trong tương lai.


6
Tất cả các đồng nghiệp của tôi đều khiến tôi phát điên vì điều này và muốn có dấu ngoặc nhọn ngay cả cho một câu lệnh đơn sau if ... (trong trường hợp bạn thêm thứ gì đó vào sau 'mà không nhận ra'). Hãy sử dụng F # và Boo (và YAML), nếu ngôn ngữ của bạn không thể đọc được nếu không có thụt lề, hãy biến nó thành một phần của ngôn ngữ, tôi nói.
Benjol

48
Thêm niềng răng hợp lý, IMO - C # chắc chắn không cố gắng ngăn nó trở thành vấn đề. Điều đó rất khác với vấn đề "biến == hằng số".
Jon Skeet

6
a if (this! = that) {performanceAsSuch (); } thực sự là khá khỏe mạnh. Lập luận về các đường nối "bổ sung sau" đối với tôi về việc mong manh như không tránh / spoting nếu (a = 5) lỗi chính tả
jpinto3912

3
@jpinto: Tôi không thực sự chắc chắn những gì bạn đang cố gắng để nói ở đây - mà bạn làm như niềng răng xung quanh tuyên bố duy nhất, hoặc là bạn không? Sự khác biệt giữa điều này và nghiệp vụ biến trong câu hỏi là trong C #, mã có lỗi đánh máy là mã không hợp lệ nên trình biên dịch sẽ dừng nó. Điều này cũng không đúng đối với việc không niềng răng.
Jon Skeet

3
@Benjol Đừng quên luôn cung cấp một else { }mệnh đề trống trong trường hợp ai đó cần thêm thứ gì đó vào đó sau đó và quên tự viết elsetừ khóa. (Trò đùa)
Jeppe Stig Nielsen

12

Có một lý do chính đáng để sử dụng null trước: if(null == myDuck)

Nếu bạn class Duckghi đè ==toán tử, thì if(myDuck == null)có thể đi vào một vòng lặp vô hạn.

Sử dụng nulllần đầu tiên sử dụng một bộ so sánh bình đẳng mặc định và thực sự làm những gì bạn đang có ý định.

(Tôi nghe nói rằng cuối cùng bạn đã quen với việc đọc mã được viết theo cách đó - tôi chỉ là chưa trải nghiệm sự chuyển đổi đó).

Đây là một ví dụ:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}

11
Cái này sai. Bị phản đối. Chỉ cần trỏ chuột qua ==toán tử và đặt nó ở đó, và bạn sẽ thấy rằng quá tải do người dùng xác định được sử dụng trong cả hai trường hợp. Tất nhiên nó có thể tạo ra một sự khác biệt nhỏ nếu bạn kiểm tra atrước bvà sau đó hoán đổi vị trí của atừ toán hạng bên phải sang toán hạng bên trái. Nhưng bạn cũng có thể kiểm tra btrước a, và sau đó b == nullsẽ là thứ đã đảo ngược thứ tự. Điều này là hoàn toàn khó hiểu khi để nhà điều hành tự gọi. Thay vào đó, ép kiểu toán hạng (hoặc cả hai) objectđể có được quá tải mong muốn. Thích if ((object)a == null)hoặc if (a == (object)null).
Jeppe Stig Nielsen

10

Giống như mọi người đã lưu ý, nó ít nhiều đến từ ngôn ngữ C, nơi bạn có thể nhận được mã sai nếu bạn vô tình quên dấu bằng thứ hai. Nhưng có một lý do khác cũng phù hợp với C #: Khả năng đọc.

Chỉ cần lấy ví dụ đơn giản sau:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Nếu bạn chỉ cần hoán đổi tất cả các từ rỗng sang đầu, bạn có thể dễ dàng hơn nhiều trong việc kiểm tra tất cả:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Vì vậy, ví dụ này có thể là một ví dụ tồi (tham khảo hướng dẫn mã hóa) nhưng hãy nghĩ về việc bạn cuộn nhanh qua một tệp mã hoàn chỉnh. Chỉ bằng cách nhìn thấy mẫu

if(null ...

bạn ngay lập tức biết điều gì sẽ xảy ra tiếp theo.

Nếu ngược lại, bạn luôn phải quét đến cuối dòng để xem kiểm tra tính vô hiệu, chỉ để bạn vấp một giây để tìm ra loại kiểm tra được thực hiện ở đó. Vì vậy, có thể tô sáng cú pháp có thể giúp ích cho bạn, nhưng bạn luôn chậm hơn khi những từ khóa đó ở cuối dòng thay vì ở phía trước.


9

Tôi đoán đây là một lập trình viên C đã chuyển đổi ngôn ngữ.

Trong C, bạn có thể viết như sau:

int i = 0;
if (i = 1)
{
    ...
}

Lưu ý việc sử dụng một dấu bằng duy nhất ở đó, có nghĩa là mã sẽ gán 1 cho biến i, sau đó trả về 1 (một phép gán là một biểu thức) và sử dụng 1 trong câu lệnh if, sẽ được xử lý là true. Nói cách khác, ở trên là một lỗi.

Tuy nhiên, trong C #, điều này là không thể. Thực sự không có sự khác biệt giữa hai.


1
"Tuy nhiên, trong C #, điều này là không thể" nếu không có trình biên dịch phàn nàn, vì câu lệnh if yêu cầu biểu thức boolean và câu lệnh được cung cấp có kiểu int.
Robert Harvey

4

Trong thời gian trước đó, mọi người sẽ quên dấu '!' (hoặc dấu '=' bổ sung cho bằng nhau, khó phát hiện hơn) và thực hiện một bài tập thay vì so sánh. đặt null ở phía trước loại bỏ khả năng xảy ra lỗi, vì null không phải là giá trị l (IE không thể gán nó cho).

Hầu hết các trình biên dịch hiện đại đều đưa ra cảnh báo khi bạn thực hiện một nhiệm vụ trong điều kiện ngày nay và C # thực sự đưa ra lỗi. Hầu hết mọi người chỉ gắn bó với lược đồ var == null vì nó dễ đọc hơn đối với một số người.


Tất cả các trình biên dịch C # (lưu ý đây là câu hỏi C #) sẽ không thể biên dịch câu lệnh "if" trong đó biểu thức không phải là Boolean mặc dù ...
Jon Skeet

4

Tôi không thấy bất kỳ lợi ích nào khi tuân theo quy ước này. Trong C, nơi các kiểu boolean không tồn tại, rất hữu ích khi viết

if( 5 == variable)

hơn là

if (variable == 5)

bởi vì nếu bạn quên một trong các dấu hiệu, bạn sẽ kết thúc với

if (variable = 5)

chỉ định 5 cho biến và luôn đánh giá là true. Nhưng trong Java, một boolean là một boolean. Và với! =, Không có lý do gì cả.

Tuy nhiên, một lời khuyên tốt là hãy viết

if (CONSTANT.equals(myString))

hơn là

if (myString.equals(CONSTANT))

vì nó giúp tránh NullPointerExceptions.

Lời khuyên của tôi là yêu cầu một sự biện minh của quy tắc. Nếu không có, tại sao theo dõi nó? Nó không giúp dễ đọc


0

Đối với tôi, đó luôn là phong cách bạn thích

@Shy - Sau đó, nếu bạn nhầm lẫn giữa các toán tử thì bạn nên nhận lỗi biên dịch hoặc bạn sẽ chạy mã với một lỗi - một lỗi sẽ quay lại và cắn bạn sau đó vì nó tạo ra hành vi không mong muốn


Tôi không nghĩ rằng tôi đã từng nghe nói về bất cứ ai thích sự "liên tục == biến" hình thức khác hơn vì lý do an toàn, mà đã được xử lý trong C #.
Jon Skeet

Bản thân tôi thích "biến == hằng số" hơn, nhưng tôi đã thấy các lập trình viên áp dụng cách đảo ngược vì họ cảm thấy nó đọc tự nhiên hơn đối với họ.
TheCodeJunkie

1
Họ đều là những người có nhiều năm tẩy não bằng C đằng sau họ, hay đó là một sự lựa chọn chính hãng?
Jon Skeet

0

Như nhiều người đã chỉ ra, nó chủ yếu nằm trong mã C cũ, nó được sử dụng để xác định lỗi biên dịch, vì trình biên dịch chấp nhận nó là hợp pháp

Ngôn ngữ lập trình mới như java, go đủ thông minh để nắm bắt các lỗi biên dịch như vậy

Người ta không nên sử dụng "null! = Biến" như các điều kiện trong mã vì nó rất khó đọc


-4

Một điều nữa ... Nếu bạn đang so sánh một biến với một hằng số (số nguyên hoặc chuỗi cho ví dụ:), đặt hằng số ở bên trái là một phương pháp hay vì bạn sẽ không bao giờ gặp phải NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

trong khi

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Bây giờ, vì theo mặc định C # cài đặt tất cả các biến, bạn sẽ không gặp vấn đề đó trong ngôn ngữ đó.


1
Tôi không chắc bạn đang sử dụng trình biên dịch nào cho bit mã đầu tiên, nhưng nó nên biên dịch (nói chung là có cảnh báo). Giá trị của i là không xác định, nhưng nó có một số giá trị.
Dolphin

Tất nhiên cả hai mã sẽ biên dịch! Đó chính xác là vấn đề. Hãy thử chạy các mã đầu tiên và bạn sẽ hiểu những gì tôi có nghĩa là bởi "ngoại lệ huy động" ...
karlipoppins

cũng không nhận được sự khác biệt giữa hai. Bạn có thể hợp tác?
mfazekas

Không có ngoại lệ trừ khi bạn khai báo int * i trong C / C ++, và thậm chí trong trường hợp đó, nó sẽ so sánh các con trỏ và không ném ra bất kỳ ngoại lệ nào ... Giống như Dolphin đã nêu giá trị là không xác định nhưng nó sẽ không tạo ra bất kỳ ngoại lệ nào.
Cobusve

không có ngoại lệ, giả sử ilà một giá trị ngẫu nhiên không có khởi tạo thích hợp. biểu thức có ngữ nghĩa hoàn toàn giống nhau trong c / c ++
Raffaello.
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.