Tại sao chúng ta phải xác định cả == và! = Trong C #?


346

Trình biên dịch C # yêu cầu rằng bất cứ khi nào một loại tùy chỉnh xác định toán tử ==, nó cũng phải xác định !=(xem tại đây ).

Tại sao?

Tôi tò mò muốn biết lý do tại sao các nhà thiết kế nghĩ rằng nó cần thiết và tại sao trình biên dịch không thể mặc định triển khai hợp lý cho một trong hai toán tử khi chỉ có các toán tử khác hiện diện. Ví dụ, Lua cho phép bạn chỉ xác định toán tử đẳng thức và bạn nhận được cái khác miễn phí. C # có thể làm tương tự bằng cách yêu cầu bạn xác định == hoặc cả hai == và! = Và sau đó tự động biên dịch toán tử! = Như !(left == right).

Tôi hiểu rằng có những trường hợp góc kỳ lạ trong đó một số thực thể có thể không bằng nhau hoặc không bằng nhau, (như của IEEE-754 NaN), nhưng những trường hợp đó có vẻ như là ngoại lệ, không phải là quy tắc. Vì vậy, điều này không giải thích tại sao các nhà thiết kế trình biên dịch C # đưa ra ngoại lệ quy tắc.

Tôi đã thấy các trường hợp tay nghề kém trong đó toán tử đẳng thức được xác định, thì toán tử bất đẳng thức là một bản sao dán với mỗi và mọi so sánh đảo ngược và mọi && chuyển sang một | | (về cơ bản, bạn nhận được điểm ... (a == b) được mở rộng thông qua quy tắc của De Morgan). Đó là thực tế kém mà trình biên dịch có thể loại bỏ theo thiết kế, như trường hợp của Lua.

Lưu ý: Việc giữ tương tự cho các toán tử <> <=> =. Tôi không thể tưởng tượng các trường hợp bạn sẽ cần xác định những điều này theo những cách không tự nhiên. Lua cho phép bạn chỉ xác định <và <= và định nghĩa> = và> một cách tự nhiên thông qua phủ định của trình định dạng. Tại sao C # không làm như vậy (ít nhất là 'theo mặc định')?

BIÊN TẬP

Rõ ràng có những lý do hợp lệ để cho phép lập trình viên thực hiện kiểm tra sự bình đẳng và bất bình đẳng theo cách họ muốn. Một số câu trả lời chỉ ra những trường hợp có thể tốt.

Tuy nhiên, hạt nhân của câu hỏi của tôi là tại sao điều này bắt buộc phải có trong C # khi thường nó không cần thiết về mặt logic ?

Nó cũng trái ngược với các lựa chọn thiết kế cho các giao diện .NET như Object.Equals, IEquatable.Equals IEqualityComparer.Equalstrong đó việc thiếu NotEqualsđối tác cho thấy khung công tác coi !Equals()các đối tượng là không đồng đều và đó là điều đó. Hơn nữa, các lớp như Dictionaryvà các phương thức như .Contains()phụ thuộc hoàn toàn vào các giao diện đã nói ở trên và không sử dụng trực tiếp các toán tử ngay cả khi chúng được định nghĩa. Trong thực tế, khi ReSharper tạo ra các thành viên bình đẳng, nó xác định cả hai ==!=về mặt Equals()và thậm chí chỉ khi người dùng chọn tạo các toán tử. Các toán tử đẳng thức không cần thiết bởi khung để hiểu sự bình đẳng của đối tượng.

Về cơ bản, .NET framework không quan tâm đến các toán tử này, nó chỉ quan tâm đến một vài Equalsphương thức. Quyết định yêu cầu cả hai toán tử == và! = Được xác định song song bởi người dùng hoàn toàn liên quan đến thiết kế ngôn ngữ và không liên quan đến ngữ nghĩa đối với .NET.


24
+1 cho một câu hỏi xuất sắc. Dường như không có bất kỳ lý do nào cả ... tất nhiên trình biên dịch có thể cho rằng a! = B có thể được dịch sang! (A == b) trừ khi nó được nói khác đi. Tôi tò mò muốn biết tại sao họ cũng quyết định làm điều này.
Patrick87

16
Đây là cách nhanh nhất tôi thấy một câu hỏi được bình chọn và ủng hộ.
Christopher Currens

26
Tôi chắc chắn @Eric Lippert cũng có thể cung cấp câu trả lời đúng đắn.
Yuck

17
"Nhà khoa học không phải là người đưa ra câu trả lời đúng, anh ta là người hỏi đúng câu hỏi". Đây là lý do tại sao trong SE, các câu hỏi được khuyến khích. Và nó hoạt động!
Adriano Carneiro

4
Câu hỏi tương tự dành cho Python: stackoverflow.com/questions/4969629/
Josh Lee

Câu trả lời:


161

Tôi không thể nói cho các nhà thiết kế ngôn ngữ, nhưng từ những gì tôi có thể suy luận, có vẻ như đó là quyết định thiết kế phù hợp, có chủ ý.

Nhìn vào mã F # cơ bản này, bạn có thể biên dịch mã này thành một thư viện làm việc. Đây là mã hợp pháp cho F # và chỉ làm quá tải toán tử đẳng thức, không phải bất đẳng thức:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

Điều này không chính xác những gì nó trông giống như. Nó chỉ tạo ra một bộ so sánh đẳng thức ==và kiểm tra xem các giá trị bên trong của lớp có bằng nhau không.

Trong khi bạn không thể tạo một lớp như thế này trong C #, bạn có thể sử dụng một lớp được biên dịch cho .NET. Rõ ràng là nó sẽ sử dụng toán tử quá tải của chúng tôi cho == Vì vậy, thời gian chạy sử dụng để làm !=gì?

Tiêu chuẩn C # EMCA có cả đống quy tắc (phần 14.9) giải thích cách xác định toán tử nào sẽ sử dụng khi đánh giá đẳng thức. Để đơn giản hóa nó và do đó không hoàn toàn chính xác, nếu các loại được so sánh là cùng loại có một toán tử đẳng thức quá tải, nó sẽ sử dụng quá tải đó và không phải là toán tử đẳng thức tham chiếu chuẩn được kế thừa từ Object. Do đó, không có gì ngạc nhiên khi chỉ có một trong số các toán tử có mặt, nó sẽ sử dụng toán tử đẳng thức tham chiếu mặc định, mà tất cả các đối tượng đều có, không có quá tải cho nó. 1

Biết rằng đây là trường hợp, câu hỏi thực sự là: Tại sao cái này được thiết kế theo cách này và tại sao trình biên dịch không tự tìm ra nó? Rất nhiều người nói rằng đây không phải là một quyết định thiết kế, nhưng tôi muốn nghĩ rằng nó đã được nghĩ ra theo cách này, đặc biệt là về thực tế tất cả các đối tượng có một toán tử đẳng thức mặc định.

Vậy, tại sao trình biên dịch không tự động tạo !=toán tử? Tôi không thể biết chắc chắn trừ khi ai đó từ Microsoft xác nhận điều này, nhưng đây là điều tôi có thể xác định từ lý do trên thực tế.


Để ngăn chặn hành vi bất ngờ

Có lẽ tôi muốn làm một so sánh giá trị ==để kiểm tra sự bình đẳng. Tuy nhiên, khi nói đến !=tôi không quan tâm chút nào nếu các giá trị bằng nhau trừ khi tham chiếu bằng nhau, vì chương trình của tôi coi chúng bằng nhau, tôi chỉ quan tâm nếu các tham chiếu khớp với nhau. Rốt cuộc, điều này thực sự được phác thảo là hành vi mặc định của C # (nếu cả hai toán tử không bị quá tải, như trong trường hợp một số thư viện .net được viết bằng ngôn ngữ khác). Nếu trình biên dịch tự động thêm mã, tôi không còn có thể dựa vào trình biên dịch để mã đầu ra phải tuân thủ. Trình biên dịch không nên viết mã ẩn làm thay đổi hành vi của bạn, đặc biệt khi mã bạn đã viết nằm trong các tiêu chuẩn của cả C # và CLI.

Về mặt nó buộc bạn phải quá tải nó, thay vì đi đến hành vi mặc định, tôi chỉ có thể nói chắc chắn rằng nó nằm trong tiêu chuẩn (EMCA-334 17.9.2) 2 . Các tiêu chuẩn không xác định lý do tại sao. Tôi tin rằng điều này là do thực tế là C # mượn nhiều hành vi từ C ++. Xem bên dưới để biết thêm về điều này.


Khi bạn ghi đè !===, bạn không phải trả về bool.

Đây là một lý do có khả năng khác. Trong C #, chức năng này:

public static int operator ==(MyClass a, MyClass b) { return 0; }

có giá trị như cái này:

public static bool operator ==(MyClass a, MyClass b) { return true; }

Nếu bạn trả về một cái gì đó không phải là bool, trình biên dịch không thể tự động suy ra một kiểu ngược lại. Hơn nữa, trong trường hợp điều hành của bạn không trở lại bool, nó chỉ không có ý nghĩa đối với họ tạo ra mã mà sẽ chỉ tồn tại trong đó một trường hợp cụ thể hoặc, như tôi đã nói ở trên, mã mà da hành vi mặc định của CLR.


C # mượn nhiều từ C ++ 3

Khi C # được giới thiệu, có một bài báo trên tạp chí MSDN đã viết, nói về C #:

Nhiều nhà phát triển mong muốn có một ngôn ngữ dễ viết, đọc và duy trì như Visual Basic, nhưng điều đó vẫn cung cấp sức mạnh và tính linh hoạt của C ++.

Đúng vậy, mục tiêu thiết kế cho C # là cung cấp năng lượng gần như tương đương với C ++, chỉ hy sinh một chút cho các tiện ích như an toàn loại cứng và thu gom rác. C # được mô hình hóa mạnh mẽ sau C ++.

Bạn có thể không ngạc nhiên khi biết rằng trong C ++, các toán tử đẳng thức không phải trả về bool , như trong chương trình ví dụ này

Bây giờ, C ++ không trực tiếp yêu cầu bạn quá tải toán tử bổ sung. Nếu bạn biên dịch mã trong chương trình ví dụ, bạn sẽ thấy nó chạy không có lỗi. Tuy nhiên, nếu bạn đã thử thêm dòng:

cout << (a != b);

bạn sẽ nhận được

lỗi trình biên dịch C2678 (MSVC): binary '! =': không tìm thấy toán tử nào có toán hạng bên trái loại 'Test' (hoặc không có chuyển đổi chấp nhận được) `.

Vì vậy, mặc dù bản thân C ++ không yêu cầu bạn quá tải theo cặp, nhưng nó sẽ không cho phép bạn sử dụng toán tử đẳng thức mà bạn chưa quá tải trên một lớp tùy chỉnh. Nó hợp lệ trong .NET, vì tất cả các đối tượng đều có mặc định; C ++ thì không.


1. Là một lưu ý phụ, tiêu chuẩn C # vẫn yêu cầu bạn quá tải cặp toán tử nếu bạn muốn quá tải một trong hai toán tử. Đây là một phần của tiêu chuẩn và không chỉ đơn giản là trình biên dịch . Tuy nhiên, các quy tắc tương tự liên quan đến việc xác định nhà điều hành nào sẽ gọi áp dụng khi bạn truy cập thư viện .net được viết bằng ngôn ngữ khác không có cùng yêu cầu.

2. EMCA-334 (pdf) ( http: //www.ecma-i Intl.org/publications/files/ECMA-ST/Ecma-334.pdf )

3. Và Java, nhưng đó thực sự không phải là vấn đề ở đây


75
" bạn không phải trả lại bool " .. Tôi nghĩ đó là câu trả lời của chúng tôi ngay tại đó; làm tốt. Nếu! (O1 == o2) thậm chí không được xác định rõ, thì bạn không thể mong đợi tiêu chuẩn dựa vào nó.
Brian Gordon

10
Trớ trêu thay trong một chủ đề về các toán tử logic, bạn đã sử dụng một phủ định kép trong câu, "C # không cho phép bạn không ghi đè chỉ một trong hai" mà thực sự có nghĩa là "C # không cho phép bạn ghi đè chỉ một trong hai".
Dan Diplo

13
@Dan Diplo, Không sao, Tôi không bị cấm làm điều đó hay không.
Christopher Currens

6
Nếu đúng, tôi nghi ngờ # 2 là đúng; Nhưng bây giờ câu hỏi trở thành, tại sao cho phép ==trả lại bất cứ thứ gì ngoài a bool? Nếu bạn trả về một int, bạn không còn có thể sử dụng nó như một câu lệnh có điều kiện, vd. if(a == b)sẽ không còn biên dịch nữa. Vấn đề ở đây là gì?
BlueRaja - Daniel Pflughoeft

2
Bạn có thể trả về một đối tượng có chuyển đổi ngầm định thành bool (toán tử true / false) nhưng tôi vẫn không thấy điều đó sẽ hữu ích ở đâu.
Stefan Dragnev

53

Có lẽ nếu ai đó cần thực hiện logic ba giá trị (nghĩa là null). Trong các trường hợp như vậy - ví dụ SQL tiêu chuẩn ANSI - các toán tử không thể bị phủ định tùy thuộc vào đầu vào.

Bạn có thể có một trường hợp:

var a = SomeObject();

a == truetrả lại falsea == falsecũng trở về false.


1
Tôi đã nói trong trường hợp đó, vì akhông phải là một boolean, bạn nên so sánh nó với một enum ba trạng thái, không phải là thực truehay false.
Brian Gordon

18
Nhân tiện, tôi không thể tin rằng không ai tham khảo trò đùa FileNotFound ..
Brian Gordon

28
Nếu a == truefalsesau đó tôi sẽ luôn luôn mong đợi a != trueđược true, dù a == falsefalse
Fede

20
@Fede đánh vào đầu đinh. Điều này thực sự không liên quan gì đến câu hỏi - bạn đang giải thích lý do tại sao ai đó có thể muốn ghi đè ==, chứ không phải tại sao họ buộc phải ghi đè cả hai.
BlueRaja - Daniel Pflughoeft

6
@Yuck - Có, có một triển khai mặc định tốt: đó là trường hợp phổ biến. Ngay cả trong ví dụ của bạn, việc thực hiện mặc định !=sẽ là chính xác. Trong trường hợp cực kỳ hiếm gặp khi chúng tôi muốn x == yx != ycả hai đều sai (tôi không thể nghĩ ra một ví dụ duy nhất có ý nghĩa) , họ vẫn có thể ghi đè lên việc thực hiện mặc định và cung cấp riêng của họ. Luôn luôn làm cho trường hợp phổ biến là mặc định!
BlueRaja - Daniel Pflughoeft

25

Khác với C # khác với C ++ trong nhiều lĩnh vực, lời giải thích tốt nhất tôi có thể nghĩ là trong một số trường hợp, bạn có thể muốn có một cách tiếp cận hơi khác để chứng minh "không bình đẳng" hơn là chứng minh "bình đẳng".

Rõ ràng với việc so sánh chuỗi chẳng hạn, bạn chỉ có thể kiểm tra sự bằng nhau và returnra khỏi vòng lặp khi bạn thấy các ký tự không khớp. Tuy nhiên, nó có thể không được sạch sẽ với các vấn đề phức tạp hơn. Bộ lọc nở hoa đến với tâm trí; Rất dễ dàng để biết nhanh phần tử không có trong tập hợp hay không, nhưng rất khó để biết phần tử trong tập hợp hay không. Mặc dù returnkỹ thuật tương tự có thể được áp dụng, mã có thể không đẹp bằng.


6
Ví dụ tuyệt vời; Tôi nghĩ rằng bạn đang vào lý do tại sao. Một đơn giản !khóa bạn vào một định nghĩa duy nhất cho sự bình đẳng, trong đó một lập trình viên thông tin có thể có thể tối ưu hóa cả ==!=.
Yuck

13
Vì vậy, điều này giải thích tại sao họ nên được cung cấp tùy chọn ghi đè cả hai, chứ không phải tại sao họ nên bị buộc phải .
BlueRaja - Daniel Pflughoeft

1
Theo đó, họ không phải tối ưu hóa cả hai, họ chỉ cần cung cấp một định nghĩa rõ ràng về cả hai.
Jesper

22

Nếu bạn nhìn vào việc triển khai quá tải == và! = Trong nguồn .net, họ thường không thực hiện! = As! (Left == right). Họ thực hiện nó đầy đủ (như ==) với logic phủ định. Ví dụ: DateTime thực hiện == như

return d1.InternalTicks == d2.InternalTicks;

và! = như

return d1.InternalTicks != d2.InternalTicks;

Nếu bạn (hoặc trình biên dịch nếu nó làm điều đó ngầm) sẽ thực hiện! = As

return !(d1==d2);

sau đó bạn đang đưa ra một giả định về việc triển khai nội bộ của == và! = trong những điều mà lớp bạn đang tham khảo. Tránh giả định đó có thể là triết lý đằng sau quyết định của họ.


17

Để trả lời chỉnh sửa của bạn, liên quan đến lý do tại sao bạn buộc phải ghi đè cả hai nếu bạn ghi đè một, đó là tất cả trong kế thừa.

Nếu bạn ghi đè ==, rất có thể cung cấp một số loại bằng nhau về ngữ nghĩa hoặc cấu trúc (ví dụ: DateTimes bằng nhau nếu các thuộc tính InternalTicks của chúng bằng nhau thông qua chúng có thể là các trường hợp khác nhau), thì bạn đang thay đổi hành vi mặc định của toán tử từ Đối tượng, là cha mẹ của tất cả các đối tượng .NET. Toán tử == là, trong C #, một phương thức, có Object.operator (==) thực hiện cơ sở thực hiện so sánh tham chiếu. Object.operator (! =) Là một phương thức khác, khác, cũng thực hiện so sánh tham chiếu.

Trong hầu hết các trường hợp khác của phương thức ghi đè, sẽ là phi logic khi cho rằng việc ghi đè một phương thức cũng sẽ dẫn đến thay đổi hành vi thành phương pháp phản nghĩa. Nếu bạn đã tạo một lớp với các phương thức Increment () và Decrement () và overrode Increment () trong một lớp con, bạn có mong đợi Decrement () cũng bị ghi đè ngược lại với hành vi bị ghi đè của bạn không? Trình biên dịch không thể được tạo ra đủ thông minh để tạo ra hàm nghịch đảo cho bất kỳ triển khai toán tử nào trong mọi trường hợp có thể.

Tuy nhiên, các toán tử, mặc dù được triển khai rất giống với các phương thức, về mặt khái niệm hoạt động theo cặp; == và! =, <và> và <= và> =. Sẽ là phi logic trong trường hợp này theo quan điểm của người tiêu dùng khi nghĩ rằng! = Làm việc khác hơn so với ==. Vì vậy, trình biên dịch không thể được thực hiện để giả sử rằng a! = B ==! (A == b) trong mọi trường hợp, nhưng thông thường người ta mong đợi rằng == và! = Nên hoạt động theo kiểu tương tự, do đó, trình biên dịch buộc bạn thực hiện theo cặp, tuy nhiên bạn thực sự sẽ làm điều đó. Nếu, đối với lớp của bạn, a! = B ==! (A == b), thì chỉ cần thực hiện toán tử! = Bằng cách sử dụng! (==), nhưng nếu quy tắc đó không giữ trong mọi trường hợp cho đối tượng của bạn (ví dụ: , nếu so sánh với một giá trị cụ thể, bằng hoặc không bằng nhau, không hợp lệ), thì bạn phải thông minh hơn IDE.

Câu hỏi THỰC SỰ nên được đặt ra là tại sao <và> và <= và> = là các cặp cho các toán tử so sánh phải được thực hiện đồng thời, khi theo thuật ngữ số! (A <b) == a> = b và! (A> b) == a <= b. Bạn nên được yêu cầu thực hiện cả bốn nếu bạn ghi đè một và bạn cũng có thể được yêu cầu ghi đè == (và! =), Bởi vì (a <= b) == (a == b) nếu a là về mặt ngữ nghĩa bằng b.


14

Nếu bạn quá tải == cho loại tùy chỉnh của mình chứ không phải! = Thì nó sẽ được xử lý bởi toán tử! = Đối tượng! = Vì mọi thứ đều được lấy từ đối tượng và điều này sẽ khác nhiều so với CustomType! = CustomType.

Ngoài ra, những người sáng tạo ngôn ngữ có thể muốn nó theo cách này để cho phép linh hoạt nhất cho các lập trình viên, và cũng để họ không đưa ra các giả định về những gì bạn định làm.


Bạn đã truyền cảm hứng cho câu hỏi này: stackoverflow.com/questions/6919259/ từ
Brian Gordon

2
Lý do linh hoạt cũng có thể được áp dụng cho rất nhiều tính năng tuyệt vời mà họ đã quyết định loại bỏ, ví dụ blog.msdn.com/b/ericlippert/archive/2011/06/23/. Vì vậy, nó không giải thích sự lựa chọn của họ cho việc thực hiện các toán tử bình đẳng.
Stefan Dragnev

Đó là một điều rất thú vị. Có vẻ như nó sẽ là một tính năng hữu ích. Tôi không biết tại sao họ lại bỏ cái đó ra.
Chris Mullins

11

Đây là những gì tôi nghĩ đến đầu tiên:

  • Điều gì xảy ra nếu kiểm tra bất đẳng thức nhanh hơn nhiều so với kiểm tra đẳng thức?
  • Điều gì xảy ra nếu trong một số trường hợp bạn muốn trả lại falsecả cho ==!= (nghĩa là nếu chúng không thể được so sánh vì một số lý do)

5
Trong trường hợp đầu tiên, sau đó bạn có thể thực hiện kiểm tra đẳng thức về kiểm tra bất bình đẳng.
Stefan Dragnev

Trường hợp thứ hai sẽ phá vỡ các cấu trúc như DictionaryListnếu bạn sử dụng loại tùy chỉnh của mình với chúng.
Stefan Dragnev

2
@Stefan: Dictionarysử dụng GetHashCodeEquals, không phải các nhà khai thác. Listsử dụng Equals.
Dan Abramov

6

Chà, có lẽ nó chỉ là một sự lựa chọn thiết kế, nhưng như bạn nói, x!= ykhông nhất thiết phải giống như vậy !(x == y). Bằng cách không thêm một triển khai mặc định, bạn chắc chắn rằng bạn không thể quên thực hiện một triển khai cụ thể. Và nếu nó thực sự tầm thường như bạn nói, bạn có thể thực hiện cái này bằng cái kia. Tôi không thấy đây là 'thực hành kém'.

Có thể có một số khác biệt khác giữa C # và Lua nữa ...


1
Đó là thực hành tuyệt vời thực hiện một trong những điều khác. Tôi chỉ thấy nó được thực hiện khác - đó là dễ bị lỗi.
Stefan Dragnev

3
Đó là vấn đề của lập trình viên, không phải vấn đề của C #. Các lập trình viên không thực hiện quyền này, cũng sẽ thất bại trong các lĩnh vực khác.
GolezTrol

@GolezTrol: Bạn có thể giải thích rằng Lua tham khảo? Tôi hoàn toàn không quen thuộc với Lua, nhưng bạn tham khảo dường như gợi ý về điều gì đó.
zw324

@Ziyao Wei: OP chỉ ra rằng Lua thực hiện điều này khác với C # (Lua rõ ràng không thêm các đối tác mặc định cho các toán tử được đề cập). Chỉ cố gắng tầm thường hóa một so sánh như vậy. Nếu không có sự khác biệt nào cả thì ít nhất một trong số chúng không có quyền tồn tại. Phải nói tôi cũng không biết Lua.
GolezTrol

6

Các từ khóa trong câu hỏi của bạn là " tại sao " và " phải ".

Kết quả là:

Trả lời theo cách này bởi vì họ đã thiết kế nó là như vậy, là sự thật ... nhưng không trả lời phần "tại sao" của câu hỏi của bạn.

Trả lời rằng đôi khi có thể hữu ích để ghi đè cả hai điều này một cách độc lập, là đúng ... nhưng không trả lời phần "phải" trong câu hỏi của bạn.

Tôi nghĩ rằng câu trả lời đơn giản là không có bất kỳ lý do thuyết phục nào tại sao C # yêu cầu bạn ghi đè cả hai.

Ngôn ngữ sẽ chỉ cho phép bạn ghi đè ==và cung cấp cho bạn cách triển khai mặc định !=đó là ngôn ngữ !đó. Nếu bạn tình cờ muốn ghi đè !=là tốt, có tại đó.

Đó không phải là một quyết định tốt. Con người thiết kế ngôn ngữ, con người không hoàn hảo, C # không hoàn hảo. Nhún vai và QED


5

Chỉ cần thêm vào các câu trả lời tuyệt vời ở đây:

Hãy xem xét những gì sẽ xảy ra trong trình gỡ lỗi , khi bạn cố gắng bước vào một !=toán tử và ==thay vào đó là một toán tử! Nói về khó hiểu!

Điều hợp lý là CLR sẽ cho phép bạn tự do bỏ qua một hoặc các toán tử khác - vì nó phải hoạt động với nhiều ngôn ngữ. Nhưng có rất nhiều ví dụ về C # không để lộ các tính năng CLR ( reflợi nhuận và người dân địa phương, ví dụ), và rất nhiều ví dụ về việc thực hiện các tính năng không có trong CLR bản thân (ví dụ: using, lock, foreach, vv).


3

Ngôn ngữ lập trình là sự sắp xếp lại cú pháp của câu lệnh logic đặc biệt phức tạp. Với ý nghĩ đó, bạn có thể định nghĩa một trường hợp bình đẳng mà không định nghĩa một trường hợp không bình đẳng không? Câu trả lời là không. Để một đối tượng a bằng với đối tượng b, thì nghịch đảo của đối tượng a không bằng b cũng phải đúng. Một cách khác để thể hiện điều này là

if a == b then !(a != b)

điều này cung cấp khả năng xác định cho ngôn ngữ để xác định sự bình đẳng của các đối tượng. Chẳng hạn, phép so sánh NULL! = NULL có thể đưa cờ lê vào định nghĩa của hệ thống đẳng thức không thực hiện câu lệnh không đẳng thức.

Bây giờ, liên quan đến ý tưởng! = Đơn giản chỉ là định nghĩa có thể thay thế như trong

if !(a==b) then a!=b

Tôi không thể tranh luận với điều đó. Tuy nhiên, rất có thể quyết định của nhóm đặc tả ngôn ngữ C # rằng lập trình viên buộc phải xác định rõ ràng sự bình đẳng và không bình đẳng của một đối tượng với nhau


Nullsẽ chỉ là một vấn đề bởi vì đây nulllà một đầu vào hợp lệ ==, mặc dù rõ ràng là nó không thuận tiện. Cá nhân, tôi chỉ nghĩ rằng nó cho phép số lượng lớn chương trình mơ hồ, cẩu thả.
nicodemus13

2

Trong ngắn hạn, nhất quán bắt buộc.

'==' và '! =' luôn luôn là đối lập thực sự, bất kể bạn định nghĩa chúng như thế nào, được định nghĩa như vậy theo định nghĩa bằng lời nói của chúng là "bằng" và "không bằng". Bằng cách chỉ xác định một trong số chúng, bạn mở ra một sự không nhất quán của toán tử đẳng thức trong đó cả '==' và '! =' Có thể đúng hoặc cả hai đều sai cho hai giá trị đã cho. Bạn phải xác định cả hai kể từ khi bạn chọn định nghĩa một, bạn cũng phải xác định một cách phù hợp để rõ ràng định nghĩa "bình đẳng" của bạn là gì. Giải pháp khác cho trình biên dịch là chỉ cho phép bạn ghi đè '==' OR '! =' Và để lại cái khác như là phủ định cái khác. Rõ ràng, đó không phải là trường hợp với trình biên dịch C # và tôi chắc chắn là có '

Câu hỏi bạn nên đặt ra là "tại sao tôi cần ghi đè các toán tử?" Đó là một quyết định mạnh mẽ để đưa ra đòi hỏi lý luận mạnh mẽ. Đối với các đối tượng, '==' và '! =' So sánh theo tham chiếu. Nếu bạn ghi đè lên chúng để KHÔNG so sánh bằng tham chiếu, thì bạn đang tạo ra sự không nhất quán của toán tử chung mà không rõ ràng đối với bất kỳ nhà phát triển nào khác sẽ kiểm tra mã đó. Nếu bạn đang cố gắng đặt câu hỏi "trạng thái của hai trường hợp này có tương đương không?," Thì bạn nên triển khai IEqu tương thích, xác định Equals () và sử dụng lệnh gọi phương thức đó.

Cuối cùng, IEquitable () không định nghĩa NotEquals () cho cùng một lý do: tiềm năng mở ra sự không nhất quán của toán tử đẳng thức. NotEquals () LUÔN LUÔN trả về! Bằng (). Bằng cách mở ra định nghĩa của NotEquals () cho lớp triển khai Equals (), bạn một lần nữa buộc vấn đề về tính nhất quán trong việc xác định đẳng thức.

Chỉnh sửa: Đây đơn giản là lý luận của tôi.


Điều này là phi logic. Nếu lý do là nhất quán thì điều ngược lại sẽ được áp dụng: bạn sẽ chỉ có thể thực hiện operator ==và trình biên dịch sẽ suy ra operator !=. Rốt cuộc, đây là những gì nó làm operator +operator +=.
Konrad Rudolph

1
foo + = thanh; là một phép gán ghép, tốc ký cho foo = foo + bar;. Nó không phải là một nhà điều hành.
xay sinh tố

Nếu thực sự chúng "luôn luôn đối lập thực sự", thì đó sẽ là một lý do để tự động định nghĩa a != b!(a == b)Lọ (không phải là những gì C # làm).
andrewf

-3

Có lẽ chỉ là điều họ không nghĩ đến sẽ không có thời gian để làm.

Tôi luôn sử dụng phương pháp của bạn khi tôi quá tải ==. Sau đó, tôi chỉ sử dụng nó trong một cái khác.

Bạn nói đúng, với một lượng công việc nhỏ, trình biên dịch có thể cung cấp miễn phí cho chúng tôi.

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.