Tại sao C # không thể so sánh hai loại đối tượng với nhau nhưng VB thì không?


152

Tôi có hai đối tượng trong C # và không biết đó là Boolean hay bất kỳ loại nào khác. Tuy nhiên khi tôi cố gắng so sánh những C # đó không đưa ra câu trả lời đúng. Tôi đã thử mã tương tự với VB.NET và điều đó đã làm điều đó!

Bất cứ ai có thể cho tôi biết làm thế nào để khắc phục điều này nếu có một giải pháp?

C #:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True

3
Điều gì nếu bạn thay đổi so sánh bình đẳng với a.Equals(b)?
Jason Meckley

8
Đây là một câu hỏi hay cho mục đích sư phạm.
Lobo

10
Bởi vì mã VB.NET của bạn không bằng mã C # của bạn.
Chó săn an ninh

9
Khi bạn chỉ định cho abạn nhận được quyền anh và tạo ra một hộp chứa true. Khi bạn gán cho bbạn nhận được một hộp khác cũng chứa true. Khi bạn so sánh ab, vì cả hai đều thuộc loại thời gian biên dịch object, bạn gọi mức quá tải operator ==(object, object)được xác định bởi Đặc tả ngôn ngữ C #. Quá tải này kiểm tra xem các tham chiếu có đi đến cùng một đối tượng không. Vì bạn có hai hộp, kết quả là falsevà câu lệnh "bên dưới" của bạn ifsẽ không chạy. Để hiểu rõ hơn về điều này, hãy thử thay đổi sự phân công bnày: object b = a;Bây giờ bạn chỉ có một hộp.
Jeppe Stig Nielsen

3
Trước đây tôi đã từng nói "Hãy cẩn thận khi cho rằng VB.NET và C # là cùng một ngôn ngữ được nói với một giọng khác nhau - chúng không phải là"
AakashM

Câu trả lời:


168

Trong C #, ==toán tử (khi được áp dụng cho các biểu thức kiểu tham chiếu) thực hiện kiểm tra đẳng thức tham chiếu trừ khi nó bị quá tải . Bạn đang so sánh hai tài liệu tham khảo là kết quả của chuyển đổi quyền anh, vì vậy đó là những tài liệu tham khảo riêng biệt.

EDIT: Với các loại quá tải ==, bạn có thể có các hành vi khác nhau - nhưng đó là dựa trên loại thời gian biên dịch của các biểu thức. Ví dụ: stringcung cấp ==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

Ở đây so sánh đầu tiên là sử dụng toán tử quá tải, nhưng thứ hai là sử dụng so sánh tham chiếu "mặc định".

Trong VB, =toán tử thực hiện nhiều công việc hơn - nó thậm chí không tương đương với việc sử dụng object.Equals(x, y), vì những thứ như Option Comparecó thể ảnh hưởng đến cách so sánh văn bản.

Về cơ bản, các nhà khai thác không hoạt động theo cùng một cách và không có ý định làm việc theo cùng một cách.


17
+1 Tôi biết bạn sẽ ở đây, bạn YÊU những câu hỏi bí ẩn này :)
Abdusalam Ben Haj

3
@AbZy: Tôi hy vọng có thể cung cấp một lời giải thích chi tiết hơn về những gì =đã làm trong VB, nhưng thông số kỹ thuật không rõ ràng lắm.
Jon Skeet

điều thú vị, nhưng thay đổi đối tượng thành động hoạt động giống như VB
VladL

4
@VladL: Có, bởi vì sau đó nó sẽ đi theo kiểu thời gian thực hiện và thực hiện bool == boolso sánh.
Jon Skeet

1
@Mahdi Lobo có thể đã cung cấp mã, nhưng câu trả lời của anh ta cũng sai, không giống như của Jon.
Phục vụ

79

Ngoài câu trả lời của Jon giải thích khía cạnh C # của mọi thứ, đây là những gì VB làm:

Trong VB với Option Strict On, một so sánh thông qua = luôn kiểm tra sự bằng nhau về giá trị và không bao giờ cho sự bình đẳng tham chiếu. Thực tế, mã của bạn thậm chí không biên dịch khi bạn chuyển đổi Option Strict Onbởi vìSystem.Object không xác định Operator=. Bạn nên luôn luôn có tùy chọn này, nó bắt lỗi hiệu quả hơn một con ruồi venus (mặc dù trong trường hợp cụ thể của bạn, hành vi lỏng lẻo này thực sự làm đúng). 1

Trong thực tế, với Option Strict On, VB hành xử thậm chí còn nghiêm ngặt hơn C #: Trong C #,a == b hoặc kích hoạt một cuộc gọi đến SomeType.operator==(a, b)hoặc, nếu điều này không tồn tại, gọi phép so sánh đẳng thức tham chiếu (tương đương với gọi object.ReferenceEquals(a, b)).

Mặt khác, trong VB, phép so sánh a = b luôn gọi toán tử đẳng thức. 2 Nếu bạn muốn sử dụng so sánh đẳng thức tham chiếu, bạn phải sử dụng a Is b(nghĩa là, một lần nữa, giống như Object.ReferenceEquals(a, b)).


1) Đây là một dấu hiệu tốt tại sao sử dụng Option Strict Offlà một ý tưởng tồi: Tôi đã sử dụng VB.NET trong gần một thập kỷ, từ trước khi phát hành chính thức của .NET cho đến vài năm trước, và tôi hoàn toàn không biếta = b phải làm gì Option Strict Off. Nó thực hiện một số loại so sánh bình đẳng, nhưng chính xác những gì xảy ra và tại sao, không có ý tưởng. Tuy nhiên, nó phức tạp hơn dynamictính năng của C # (vì điều đó phụ thuộc vào API được ghi chép tốt). Đây là những gì MSDN nói:

Option Strict Oncung cấp kiểu gõ mạnh , ngăn chặn các chuyển đổi loại ngoài ý muốn bị mất dữ liệu, không tuân thủ ràng buộc muộn và cải thiện hiệu suất, nên sử dụng mạnh mẽ.

2) Jon đã đề cập đến một ngoại lệ, các chuỗi, trong đó so sánh bình đẳng thực hiện một số điều nữa vì lý do tương thích ngược.


4
+1. Tôi nghĩ rằng đây là một trường hợp mà các nhà thiết kế của VB.NET đã thành công trong việc tạo ra ngôn ngữ "chỉ hoạt động" cho các lập trình viên đến từ VB6 và VBA, trong đó OOP ít nổi bật hơn và vì vậy khái niệm về đẳng thức tham chiếu ít quan trọng hơn nhiều. Một lập trình viên VB có thể viết mã làm việc tốt mà không cần suy nghĩ nhiều về các đối tượng và vv.
John M Gant

5
+1 Điều này không được nâng cấp nhiều như nó thực sự cần. Không sử dụng Option Strict Onphải được coi là phạm tội hình sự ...
Deer Hunter

1
@JohnMGant: Một lập trình viên không hiểu tầm quan trọng của danh tính tham chiếu có thể viết mã xảy ra để làm việc, nhưng không thể thực sự biết những gì có thể được thay đổi một cách an toàn, những thay đổi sẽ luôn phá vỡ mọi thứ và những thay đổi có thể có vẻ hoạt động nhưng gây ra tác dụng phụ khó chịu không mong muốn (ví dụ: gây ra những gì nên được tham chiếu đến các đối tượng có thể thay đổi khác nhau có cùng trạng thái để thay vào đó là tham chiếu đến cùng một đối tượng). Nếu các đối tượng hiếm khi bị đột biến, sự thay đổi như vậy có thể không gây ra bất kỳ vấn đề nào ngay lập tức, nhưng có thể khiến các lỗi khó tìm thấy xuất hiện sau đó.
supercat

4

Các thể hiện đối tượng không được so sánh với toán tử "==". Bạn nên sử dụng phương pháp "bằng". Với toán tử "==" đang so sánh các tham chiếu, không phải các đối tượng.

Thử cái này:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Các kết quả:

a reference is not equal to b reference
a object is not equal to b object

Bây giờ, hãy thử điều này:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Các kết quả:

a reference is not equal to b reference
a object is equal to b object

1
Điều này chỉ đơn giản là vì bạn đã không ghi đè operator ==. Nếu bạn áp đảo toán tử đó và không bằng thì đầu ra của bạn sẽ bị đảo ngược. Không có gì cố hữu để so sánh tham chiếu về operator ==và không có gì vốn có về việc so sánh các giá trị trong Equals. Chúng chỉ là hai cách xác định sự bình đẳng; cả hai đều có các triển khai mặc định của một so sánh tham chiếu và cả hai đều có thể được ghi đè để làm bất cứ điều gì bạn muốn chúng làm. Sự khác biệt duy nhất Equalslà ảo, và operator ==không.
Phục vụ

1
@Servy: Lưu ý rằng bạn không thể ghi đè == - bạn chỉ có thể quá tải nó.
Jon Skeet

1
Xin lỗi, -1. Câu trả lời này đơn giản là không chính xác và nó không nên được chấp nhận.
Konrad Rudolph

Đâu đó có một câu hỏi Java đang chờ câu trả lời này.
Chad Schouggins

3

Vấn đề là toán tử == trong C # là một cuộc gọi đến một phương thức tĩnh (tốt, có thể không phải về mặt kỹ thuật, nhưng nó có thể là như vậy) dựa trên loại thời gian biên dịch của hai tham số. Những kiểu thời gian chạy thực tế của những đối tượng đó không quan trọng.

Dựa trên kiểu thời gian biên dịch đó, trình biên dịch sẽ xác định việc triển khai operator ==sử dụng. Nó có thể sử dụng objecttriển khai mặc định , nó có thể sử dụng một trong các tình trạng quá tải số do ngôn ngữ cung cấp hoặc có thể là do người dùng xác định.

Điều này khác với VB ở chỗ VB không xác định việc thực hiện tại thời điểm biên dịch. Nó chờ cho đến khi thời gian chạy và kiểm tra hai tham số được đưa ra để xác định việc triển ==khai toán tử nào sẽ sử dụng.

Mã của bạn chứa các giá trị boolean, nhưng chúng nằm trong các biến có kiểu object. Bởi vì biến là kiểu object, trình biên dịch C # sử dụng việc objectthực hiện ==, so sánh các tham chiếu , không phải các thể hiện đối tượng. Vì các giá trị boolean là các hộp, chúng không có cùng tham chiếu, mặc dù các giá trị của chúng là như nhau.

Mã VB không quan tâm loại biến là gì. Nó đợi cho đến khi thực thi và sau đó kiểm tra hai biến, thấy rằng chúng thực sự thuộc cả hai loại boolean và do đó sử dụng toán ==tử boolean . Việc triển khai đó so sánh các giá trị của booleans, chứ không phải các tham chiếu của chúng (và các booleans sẽ được bỏ hộp trước khi gọi hàm toán tử đó, do đó, một so sánh tham chiếu thậm chí không còn ý nghĩa gì nữa). Bởi vì các giá trị của booleans là như nhau, nó trả về true.


Điều đó có vẻ tốt cho C #; Tôi không biết đủ về chính xác những gì =trong VB để nói chắc chắn.
Jon Skeet

@JonSkeet Đủ công bằng.
Phục vụ

Mỗi msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , trong phần "Typeless Lập trình với Relational So sánh Toán tử": =, cùng với tất cả các nhà khai thác so sánh quan hệ khác như <, >=vv , được điều trị đặc biệt khi cả hai hoặc một trong hai bên của người vận hành Object. Điều trị đặc biệt này được thực hiện để các lập trình viên VB6, những người đã quen với việc sử dụng một loại được biết đến Varianttrong VB trước VB, có thể sử dụng ObjectVB.Net theo cách họ đã sử dụng Varianttrước đây.
rskar

Nói cách khác, và bỏ qua các tác động của quá tải và Option Strict On, VB =thiên về việc bỏ hộp Objectcho đến khi nó có thể chuyển sang Chuỗi hoặc số.
rskar
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.