Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why C# fails to compare two object types with each other but VB doesn't?

In C#, the == operator (when applied to reference type expressions) performs a reference equality check unless it's overloaded. You're comparing two references which are the result of boxing conversions, so those are distinct references.

EDIT: With types which overload the ==, you can get different behaviour - but that's based on the compile-time type of the expressions. For example, string provides ==(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

Here the first comparison is using the overloaded operator, but the second is using the "default" reference comparison.

In VB, the = operator does a whole lot more work - it's not even just equivalent to using object.Equals(x, y), as things like Option Compare can affect how text is compared.

Fundamentally the operators don't work the same way and aren't intended to work the same way.


In addition to Jon’s answer which explains the C# side of things, here’s what VB does:

In VB with Option Strict On, a comparison via = always tests for value equality and never for reference equality. In fact, your code doesn’t even compile once you switch Option Strict On because System.Object doesn’t define an Operator=. You should always have this option on, it catches bugs more effectively than a venus flytrap (although in your particular case this lax behaviour actually does the right thing).1

In fact, with Option Strict On, VB behaves even stricter than C#: In C#, a == b either triggers a call to SomeType.operator==(a, b) or, if this doesn’t exist, invokes reference equality comparison (which is equivalent to calling object.ReferenceEquals(a, b)).

In VB on the other hand, the comparison a = b always invokes the equality operator.2 If you want to use reference equality comparison, you have to use a Is b (which is, once again, the same as Object.ReferenceEquals(a, b)).


1) Here’s a good indication why using Option Strict Off is a bad idea: I’ve used VB.NET for almost a decade, from before .NET’s official release until a few years ago, and I’ve absolutely no idea what a = b does with Option Strict Off. It does some kind of equality comparison, but what exactly happens and why, no idea. It’s more complex than C#’s dynamic feature, though (because that relies on a well-documented API). Here’s what the MSDN says:

Because Option Strict On provides strong typing, prevents unintended type conversions with data loss, disallows late binding, and improves performance, its use is strongly recommended.

2) Jon has mentioned one exception, strings, where equality comparison does some more things for reasons of backwards compatibility.


Object instances are not compared with the operator "==". You should to use method "equals". With "==" operator are comparing references, not objects.

Try this:

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");
}

Results:

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

Now, try this:

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");
}

Results:

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

The issue is that the == operator in C# is a call to a static method (well, maybe not technically, but it can be though of as such) based on the compile time type of the two parameters. What the actual runtime types of those objects are doesn't matter.

Based on that compile time type the compiler will determine what implementation of operator == to use. It might use the default object implementation, it might use one of the numeric overloads provided by the language, or it could be a user defined implementation.

This is different from VB in that VB doesn't determine the implementation at compile time. It waits until runtime and inspects the two parameters that it is given to determine which implementation of the == operator it should use.

Your code contains boolean values, but they are in variables that are of type object. Because the variable is of type object, the C# compiler use the object implementation of ==, which compares the references, not the object instances. Since the boolean values are boxes, they don't have the same reference, even though their values are the same.

The VB code doesn't care what type the variable is. It waits until runtime and then checks the two variables, sees that they are actually of both of type boolean, and so uses the boolean == operator implementation. That implementation compares the values of the booleans, not their references (and the booleans will be unboxed before calling calling that operator, so a reference comparison doesn't even make sense any more). Because the values of the booleans are the same, it returns true.