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
Omer van Kloeten
  • 11,800
  • 9
  • 42
  • 53
Mohsen Sarkar
  • 5,910
  • 7
  • 47
  • 86
  • 3
    what if you change the equality comparer to `a.Equals(b)`? – Jason Meckley Feb 12 '13 at 16:35
  • 8
    This is a good question for pedagogical purposes. – Lobo Feb 12 '13 at 17:06
  • 10
    Because your VB.NET code is not equal to your C# code. – Security Hound Feb 12 '13 at 17:07
  • 9
    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. – Jeppe Stig Nielsen Feb 12 '13 at 21:58
  • 3
    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" – AakashM Feb 13 '13 at 10:49

4 Answers4

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.

Tarik
  • 79,711
  • 83
  • 236
  • 349
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 4
    +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. – John M Gant Feb 12 '13 at 22:07
  • 5
    +1 This isn't upvoted as much as it really should. Not using `Option Strict On` has to be considered a criminal offense... – Deer Hunter Feb 13 '13 at 03:34
  • 1
    @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. – supercat Aug 14 '13 at 18:00
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
Lobo
  • 4,001
  • 8
  • 37
  • 67
  • 1
    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. – Servy Feb 12 '13 at 17:17
  • 1
    @Servy: Note that you can't *override* `==` - you can only *overload* it. – Jon Skeet Feb 12 '13 at 17:26
  • 1
    Sorry, -1. This answer is simply incorrect and it shouldn’t be the accepted one. – Konrad Rudolph Feb 12 '13 at 19:45
  • Somewhere there is a Java question waiting for this answer. – Chad Schouggins Feb 19 '13 at 23:32
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.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • That looks fine for the C#; I don't know enough about exactly what `=` does in VB to say for sure. – Jon Skeet Feb 12 '13 at 17:32
  • Per http://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. – rskar Feb 12 '13 at 18:54
  • 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. – rskar Feb 12 '13 at 18:55