2010-08-17 30 views
32

Mã có sử dụng số static Object.Equals để kiểm tra null mạnh hơn mã sử dụng toán tử == hoặc regular Object.Equals không? Không phải là hai sau dễ bị bị ghi đè theo cách kiểm tra null không hoạt động như mong đợi (ví dụ: trả về false khi giá trị được so sánh null)?Bằng (mục, null) hoặc mục == null

Nói cách khác, là thế này:

if (Equals(item, null)) { /* Do Something */ } 

mạnh mẽ hơn này:

if (item == null) { /* Do Something */ } 

Cá nhân tôi thấy cú pháp sau dễ đọc hơn. Có nên tránh khi viết mã sẽ xử lý các đối tượng ngoài tầm kiểm soát của tác giả (ví dụ: thư viện) không? Nên luôn luôn tránh (khi kiểm tra null)? Đây có phải là chỉ tách tóc?

Trả lời

43

Không có câu trả lời đơn giản cho câu hỏi này. Bất cứ ai nói luôn luôn sử dụng một hoặc khác là cho bạn lời khuyên nghèo, theo ý kiến ​​của tôi.

Có một số phương pháp khác nhau mà bạn có thể gọi để so sánh các cá thể đối tượng. Với hai trường hợp đối tượng ab, bạn có thể viết:

  • Object.Equals(a,b)
  • Object.ReferenceEquals(a,b)
  • a.Equals(b)
  • a == b

tất cả Đây có thể làm những việc khác nhau!

Object.Equals(a,b) sẽ (theo mặc định) thực hiện so sánh bình đẳng tham chiếu trên loại tham chiếu và so sánh bit trên các loại giá trị.Từ các tài liệu MSDN:

Việc thực hiện mặc định của Equals hỗ trợ bình đẳng tham khảo cho loại tài liệu tham khảo, và bình đẳng Bitwise với nhiều loại giá trị. Tham chiếu bình đẳng có nghĩa là các tham chiếu đối tượng được so sánh tham chiếu đến cùng một đối tượng. Mức độ bình đẳng bằng bit có nghĩa là các đối tượng được so sánh có cùng biểu diễn nhị phân .

Lưu ý rằng loại có nguồn gốc có thể ghi đè phương thức Bằng để thực hiện bình đẳng giá trị. Giá trị bình đẳng nghĩa là các đối tượng được so sánh có cùng giá trị nhưng khác nhau biểu thị nhị phân .

Lưu ý đoạn cuối cùng ở trên ... chúng ta sẽ thảo luận về vấn đề này sau.

Object.ReferenceEquals(a,b) chỉ thực hiện so sánh bình đẳng tham chiếu. Nếu các loại được chuyển là các loại giá trị được đóng hộp, kết quả luôn là false.

a.Equals(b) gọi phương thức dụ ảo của Object, mà loại a có thể ghi đè lên làm gì nó muốn. Cuộc gọi được thực hiện bằng cách sử dụng công văn ảo, vì vậy mã chạy phụ thuộc vào loại thời gian chạy là a.

a == b gọi toán tử quá tải tĩnh của ** loại thời gian biên dịch * của a. Nếu việc thực thi toán tử đó gọi các phương thức mẫu trên a hoặc b, nó cũng có thể phụ thuộc vào kiểu tham số thời gian chạy. Kể từ khi công văn được dựa trên các loại trong biểu thức, sau đây có thể mang lại kết quả khác nhau:

Frog aFrog = new Frog(); 
Frog bFrog = new Frog(); 
Animal aAnimal = aFrog; 
Animal bAnimal = bFrog; 
// not necessarily equal... 
bool areEqualFrogs = aFrog == bFrog; 
bool areEqualAnimals = aAnimal = bAnimal; 

Vì vậy, vâng, có lỗ hổng để kiểm tra cho null sử dụng operator ==. Trong thực tế, hầu hết các loại không quá tải == - nhưng không bao giờ có bảo đảm.

Phương pháp thể hiện Equals() không tốt hơn ở đây. Mặc dù việc triển khai mặc định thực hiện kiểm tra tính bình đẳng tham chiếu/bitwise, nhưng có thể loại ghi đè phương thức thành viên Equals(), trong trường hợp này, việc triển khai này sẽ được gọi. Một người dùng được cung cấp thực hiện có thể trả lại bất cứ điều gì nó muốn, ngay cả khi so sánh với null.

Nhưng phiên bản tĩnh của Object.Equals() bạn hỏi là gì? Điều này có thể kết thúc chạy mã người dùng không? Vâng, nó chỉ ra rằng câu trả lời là CÓ. Việc thực hiện Object.Equals(a,b) mở rộng tới một cái gì đó dọc theo dòng:

((object)a == (object)b) || (a != null && b != null && a.Equals(b)) 

Bạn có thể thử này cho chính mình:

class Foo { 
    public override bool Equals(object obj) { return true; } } 

var a = new Foo(); 
var b = new Foo(); 
Console.WriteLine(Object.Equals(a,b)); // outputs "True!" 

Do đó, nó có thể cho các tuyên bố: Object.Equals(a,b) để chạy mã người dùng khi không trong số các loại cuộc gọi là null.Lưu ý rằng Object.Equals(a,b)không gọi phiên bản cá thể là Equals() khi một trong hai đối số là rỗng.

Tóm lại, loại hành vi so sánh bạn nhận được có thể thay đổi đáng kể, tùy thuộc vào phương pháp bạn chọn gọi. Một bình luận ở đây, tuy nhiên: Microsoft không chính thức tài liệu hành vi nội bộ của Object.Equals(a,b). Nếu bạn cần một ý chí sắt bọc gaurantee so sánh một tài liệu tham khảo để null mà không bất kỳ mã chạy khác, bạn muốn Object.ReferenceEquals():

Object.ReferenceEquals(item, null); 

Phương pháp này làm cho mục đích extremently rõ ràng - bạn đang đặc biệt chờ đợi kết quả được so sánh của hai tham chiếu cho bình đẳng tham chiếu. Lợi ích ở đây trên sử dụng một cái gì đó giống như Object.Equals(a,null), là nó ít có khả năng rằng ai đó sẽ đến sau cùng và nói:

"Này, đây là vụng về, chúng ta hãy thay thế nó bằng: a.Equals(null) hoặc a == null

mà có khả năng Có thể khác nhau:

Hãy tiêm một số chủ nghĩa thực dụng ở đây, tuy nhiên Cho đến nay, chúng tôi đã nói về tiềm năng của các phương thức so sánh khác nhau để mang lại kết quả khác nhau trong khi điều này chắc chắn xảy ra. an toàn để wr ite a == null. Các lớp .NET dựng sẵn như StringNullable<T> có ngữ nghĩa được xác định rõ ràng để so sánh. Hơn nữa, chúng là sealed - ngăn chặn bất kỳ thay đổi nào đối với hành vi của chúng thông qua kế thừa. Sau đây là khá phổ biến (và chính xác):

string s = ... 
if(s == null) { ... } 

Đó là không cần thiết (và xấu xí) để viết:

if(ReferenceEquals(s,null)) { ... } 

Vì vậy, trong một số trường hợp hạn chế, sử dụng == là an toàn, và phù hợp.

+0

Tôi nghĩ câu trả lời của tôi (câu trả lời của Microsoft bằng proxy) là một câu trả lời khá đơn giản. – mquander

+4

Các câu hỏi như: * "tôi nên luôn luôn/không bao giờ làm X" * ngụ ý một khoảng cách kiến ​​thức về các sắc thái của chủ đề được đề cập. Tôi cảm thấy rằng chi tiết hơn một chút sẽ hữu ích ở đây để làm rõ tại sao tôi không nghĩ một câu trả lời đơn giản là có ý nghĩa. – LBushkin

+0

Phương thức 'object.Equals (a, b)' tĩnh có thể gọi phương thức 'Equals' quá tải/ảo trên đối tượng' a'. Nếu tôi nhớ lại chính xác, nó làm một cái gì đó như '((đối tượng) a == (đối tượng) b) || (a! = null && b! = null && a.Equals (b)) '. (Điều này là không liên quan khi so sánh với 'null', đó là những gì OP hỏi, nhưng có liên quan đến trường hợp chung.) – LukeH

4

if (Equals(item, null)) không mạnh hơn if (item == null) và tôi thấy khó khởi động hơn.

+0

Có thể ai đó quá tải toán tử '==' mà không ghi đè 'Bằng '. Đây sẽ là một thiết kế tồi, nhưng hoàn toàn có thể cho ngữ nghĩa so sánh bình đẳng với sự khác biệt giữa hai.Ngoài ra, nếu toán tử '==' bị quá tải, nó có thể làm điều gì đó hoàn toàn khác với phương thức 'Objects.Equals()' được xây dựng sẵn - mà tôi tin rằng chỉ kiểm tra sự bình đẳng tham chiếu. – LBushkin

2

Các framework guidelines gợi ý rằng bạn đối xử với Equals như bình đẳng giá trị (kiểm tra để xem liệu hai đối tượng đại diện cho thông tin tương tự, tức là so sánh tài sản), và == như bình đẳng tham khảo, với ngoại lệ của đối tượng bất biến, mà bạn nên có lẽ override == là giá trị bình đẳng.

Vì vậy, giả sử các nguyên tắc áp dụng tại đây, hãy chọn bất kỳ điều gì hợp lý về mặt ngữ nghĩa. Nếu bạn đang đối phó với các đối tượng bất biến, và bạn mong đợi cả hai phương pháp để tạo ra kết quả giống hệt nhau, tôi sẽ sử dụng == để làm rõ.

+2

Nguyên tắc của Microsoft KHÔNG giải quyết vấn đề về null, đó là câu hỏi của bạn. Quan trọng nhất, trong khi "myString == null" là một thử nghiệm an toàn, "myString.Equals (null)" sẽ gây ra một ngoại lệ khi myString là null. Ngoài ra, các nguyên tắc của Microsoft thậm chí không đề cập đến sự khác biệt giữa Object.Equals (myObject, b) và myObject.Equals (b). Cái trước là mạnh mẽ; sau này đưa ra một ngoại lệ nếu myObject là null. Vì vậy, trong khi liên kết bạn cung cấp là hữu ích, nó KHÔNG phải là câu trả lời cho câu hỏi của người đăng. – ToolmakerSteve

1

Để tham chiếu đến "... mã viết sẽ xử lý các đối tượng ngoài tầm kiểm soát của tác giả ...", tôi sẽ chỉ ra rằng cả tĩnh Object.Equals và toán tử == là các phương thức tĩnh và do đó không thể ảo/ghi đè. Việc triển khai nào được gọi là được xác định tại thời điểm biên dịch dựa trên (các) loại tĩnh. Nói cách khác, không có cách nào cho một thư viện bên ngoài để cung cấp một phiên bản khác nhau của thường trình cho mã được biên dịch của bạn.

+0

** Câu lệnh này không hoàn toàn chính xác. ** Có thể thực hiện toán tử '==' trên một kiểu để gọi một phương thức ảo trên một trong các cá thể có liên quan. Có nghĩa là nó * thực sự là có thể * cho những điều xảy ra mà không dựa trên các loại trong biểu thức, mà là trên các kiểu thời gian chạy liên quan. – LBushkin

+0

Và điều tương tự cũng áp dụng cho phương thức 'object.Equals (a, b)' tĩnh, điều này giống như '((object) a == (object) b) || (a! = null && b! = null && a.Equals (b)) 'có nghĩa là, nó có thể gọi phương thức' Equals' ảo trên đối tượng 'a'. – LukeH

+0

@LBushkin Điểm của bạn là hợp lệ, tuy nhiên điều đó không bằng nhau (tha thứ cho sự chơi chữ) tuyên bố của tôi là 'không hoàn toàn chính xác'. –

1

Khi bạn muốn kiểm tra SẮC (cùng một vị trí trong bộ nhớ):

ReferenceEquals(a, b)

Xử lý null. Và không thể ghi đè. 100% an toàn.

Nhưng hãy chắc chắn bạn thực sự muốn thử nghiệm IDENTITY. Hãy xem xét những điều sau đây:

ReferenceEquals(new String("abc"), new String("abc"))

trả về false.Ngược lại:

Object.Equals(new String("abc"), new String("abc"))

(new String("abc")) == (new String("abc"))

cả trở true.

Nếu bạn đang mong đợi câu trả lời là true trong trường hợp này, thì bạn muốn kiểm tra EQUALITY, không phải là thử nghiệm IDENTITY. Xem phần tiếp theo.


Khi bạn muốn kiểm tra BÌNH ĐẲNG (nội dung giống nhau):

  • Sử dụng "a == b" nếu trình biên dịch không phàn nàn.

  • Nếu bị từ chối (nếu loại biến không xác định toán tử "=="), thì hãy sử dụng "Object.Equals(a, b)".

  • NẾU bạn đang ở trong logic nơi một được biết là không được null, sau đó bạn có thể sử dụng dễ đọc hơn "a.Equals(b)". Ví dụ: "this.Equals (b)" là an toàn. Hoặc nếu "a" là trường được khởi tạo vào thời gian xây dựng và hàm tạo sẽ ném ngoại lệ nếu giá trị NULL được chuyển vào làm giá trị được sử dụng trong trường đó.

NOW, để giải quyết các câu hỏi ban đầu:

Q: Là những dễ bị bị ghi đè trong một số lớp, với mã mà không xử lý rỗng một cách chính xác, kết quả là một ngoại lệ?

A: Có. Cách duy nhất để có được kiểm tra EQUALITY 100% an toàn là tự kiểm tra trước các giá trị null.

Nhưng phải không? Lỗi sẽ ở trong đó (lớp xấu trong tương lai giả định), và nó sẽ là một loại thất bại đơn giản. Dễ dàng để gỡ lỗi và sửa chữa (bởi bất cứ ai cung cấp các lớp học). Tôi nghi ngờ đó là một vấn đề xảy ra thường xuyên, hoặc vẫn tồn tại lâu dài khi nó xảy ra.

Chi tiết A: Object.Equals(a, b) có nhiều khả năng hoạt động khi đối mặt với lớp học kém hơn. Nếu "a" là null, lớp Object sẽ tự xử lý nó, vì vậy không có rủi ro ở đó. Nếu "b" là null, thì kiểu DYNAMIC (thời gian chạy không biên dịch) của "a" xác định phương thức "Equals" được gọi. Phương thức được gọi đơn thuần chỉ hoạt động chính xác khi "b" là null. Trừ khi phương pháp được gọi là cực kỳ kém bằng văn bản, bước đầu tiên nó làm là xác định xem "b" là một loại mà nó hiểu.

Vì vậy, Object.Equals(a, b) là sự thỏa hiệp hợp lý giữa khả năng đọc/mã hóa và an toàn.

Các vấn đề liên quan