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

4

152

I have two objects in C# and don't know if it's Boolean or any other type. However when I try to compare those C# fails to give the right answer. I have tried the same code with VB.NET and that did it !

Can anyone tell me how to fix this if there is a solution ?

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
Thresathresh answered 12/2, 2013 at 16:31 Comment(5)
what if you change the equality comparer to a.Equals(b)?Inculcate
This is a good question for pedagogical purposes.Eyeglass
Because your VB.NET code is not equal to your C# code.Keir
When you assign to a you get boxing and create a box containing true. When you assign to b you get another box also containing true. When you compare a and b, because both are of compile-time type object, you call the overload operator ==(object, object) defined by the C# Language Specification. This overload checks to see if the references go to the same object. Since you have two boxes, the result is false, and the statement "under" your if will not run. To understand this better, try to change the assignment of b to this: object b = a; Now you have just one box.Bacolod
I've had occasion before to say "Be careful assuming that VB.NET and C# are the same language spoken with a different accent - they're not"Cowitch
G
168

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.

Gombosi answered 12/2, 2013 at 16:38 Comment(10)
+1 I knew you will be around, you LOVE these kinds of mysterious questions :)Harmonist
@AbZy: I'd hoped to be able to provide a more detailed explanation of what = did in VB, but the spec isn't terribly clear.Gombosi
interesting thing, but changing object to dynamic behaves same as VBAlagez
@VladL: Yes, because then it will go by the execution-time types, and perform the bool == bool comparison.Gombosi
Thank you Jon. I have marked your answer as final answer but had to change it to Lobo answer because he also provided the solution. However thanksThresathresh
@Mahdi Lobo may have provided code, but his answer is also wrong, unlike Jon's.Elliotelliott
I'm interested in your critique of my answer. Does it accurately represent what's going on?Elliotelliott
How do you do that you answer those simple/popular question all the time? :)Selfness
@Peri When you answer 24,536 questions, some of them are bound to become popular.Elliotelliott
@Servy: Then how does he do that he answers 24,536 questions!? :)Selfness
C
79

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.

Caseworm answered 12/2, 2013 at 20:1 Comment(3)
+1. I think this is one case where the designers of VB.NET succeeded in making the language "just work" for programmers coming from VB6 and VBA, where OOP is much less prominent and so the concept of reference equality is much less important. A VB coder can write good working code without thinking much about objects and so forth.Egoist
+1 This isn't upvoted as much as it really should. Not using Option Strict On has to be considered a criminal offense...Wessels
@JohnMGant: A coder that doesn't understand the significance of reference identity may be able to write code that happens to work, but is unlikely to really know what things can safely be changed, what changes will always break things, and what changes may appear to work but cause unwanted nasty side-effects (e.g. causing what should be references to different mutable objects that have the same state to instead be references to the same object). If objects are seldom mutated, such a change may not cause any immediate problems, but may make hard-to-find bugs spring up later.Dutcher
E
4

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
Eyeglass answered 12/2, 2013 at 16:40 Comment(4)
This is simply because you didn't override operator ==. If you overrode that operator and not equals then your output would be reversed. There is nothing inherent to comparing reference about operator == and nothing inherent about comparing values in Equals. They are just two ways of determining equality; both have default implementations of a reference comparison, and both can be overridden to do whatever you want them to do. The only other difference is that Equals is virtual, and operator == is not.Elliotelliott
@Servy: Note that you can't override == - you can only overload it.Gombosi
Sorry, -1. This answer is simply incorrect and it shouldn’t be the accepted one.Caseworm
Somewhere there is a Java question waiting for this answer.Candor
E
3

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.

Elliotelliott answered 12/2, 2013 at 17:28 Comment(3)
That looks fine for the C#; I don't know enough about exactly what = does in VB to say for sure.Gombosi
Per msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx, in section "Typeless Programming with Relational Comparison Operators": =, along with all the other relational comparison operators such as <, >=, etc., are given special treatment when both or either side of the operator is Object. This special treatment is done so that VB6 programmers, who are accustomed to using a type known as Variant in pre-.NET VB, can make use of Object in VB.Net in the ways they've used Variant before.Flush
To put it another way, and leaving aside the effects of overloading and Option Strict On, VB = is biased towards unboxing an Object until it can get to a String or numeric.Flush

© 2022 - 2024 — McMap. All rights reserved.