11

I'm tryign to get my head around the use of System.Object.operator==().

My Effective C# book, and the page here (http://www.srtsolutions.com/just-what-is-the-default-equals-behavior-in-c-how-does-it-relate-to-gethashcode), says that:

"System.Object.operator==() will call a.Equals(b) to determine if a and b are equal".

So with my code:

   object a = 1;
   object b = 1;

   if(object.Equals(a, b))
   {
    // Will get here because it calls Int32.Equals(). I understand this.

   }

   if(a == b)
   {
    // I expected it to get here, but it doesn't.
   }

I expected (a == b) to call Int32's overriden Equals and compare values in the same way that static objet.Equals() does. What am I missing?

Edit: I should perhaps have added that I can see what (a == b) is testing - it's testing reference equality. I was thrown by the book which seems to suggest it will work internally much as static object.Equals(obect, object) will.

MrNick
  • 399
  • 2
  • 8
  • 16

7 Answers7

7

I'm not sure why the book would say that; it is emphatically untrue that the default == calls Equals. Additionally, object does NOT overload ==. The operator == by default performs a value-equality comparison for value types and a reference-equality comparison for reference types. Again, it is NOT overloaded for object (it is for string). Therefore, when you compare object a = 1 and object b = 1 using the == operator you are doing a reference-equality comparison. As these are different instances of a boxed int, they will compare differently.

For all that are confused by this issue, I encourage you to read §7.10 and especially §7.10.6 of the specification extremely carefully.

For more on the subtleties of boxing (or why we need it in the first place), I refer you to a previous post on this subject.

Community
  • 1
  • 1
jason
  • 236,483
  • 35
  • 423
  • 525
  • For those that just want a quick link to the C# specification from here, try http://msdn.microsoft.com/en-us/library/ms228593.aspx. – Andy Nov 18 '10 at 15:18
  • You should probably change "override" in your answer to "overload," as static methods (including operators like `==`) can't be overridden. – Dan Tao Nov 18 '10 at 15:23
  • I understand the boxing issues. The book made it sound as though a == b would have the same internal behaviour as **static** object.Equals() - they are both static functions after all, so could do the same thing. As I understand it, static object.Equals() effectively delegates at runtime to instance Equals(). The book makes out that == also does this, but I'll have to accept that it doesn't – MrNick Nov 18 '10 at 15:27
  • @MrNick: The compiler will resolve `==` into a call to the CIL method `ceq` completely subverting `Object.Equals`. The static method `Object.Equals(x, y)` on the other hand checks for reference equality, checks that both parameters are not null and then invokes `x.Equals(y)`. This is why `object a = 1; object b = 1; Console.WriteLine(a == b);` will print `false` but `object a = 1; object b = 1; Console.WriteLine(Object.Equals(a == b));` will print `true`. – jason Nov 18 '10 at 15:36
  • Thanks Jason (and Dan). Yep, the static Object.Equals(x, y) I understood, but the book made it sound like == would do the same. The quote from the book says: "Anytime you create a value type, redefine operator==(). The reason is exactly the same as with instance Equals(). The default version uses reflection to compare the contents of the two value types.". What is this 'default version' he refers to? – MrNick Nov 18 '10 at 16:03
  • @MrNick: First, there is no default `==` for user-defined value types. However, the default implementation of `ValueType.Equals` (that which all user-defined value types inherit unless they explicitly override) does use reflection in its implementation. I don't agree that one needs or has to _define_ `==` or override `Equals` for all user-defined value types. I would only override `Equals` if I needed semantics different than the default implementation of `Equals` or if profiling told me that the default implementation is a bottleneck (unlikely). – jason Nov 18 '10 at 16:25
  • Thanks. I think the bit that helped me most was when you said "The compiler will resolve == into a call to the CIL method ceq". I'm guessing that this instruction ends up comparing the data in two variables (so references for reference types, and values for value types). == does not work for structs unless an overload is provided - could ceq not have been used here? – MrNick Nov 18 '10 at 17:11
2

As the object type doesn't override == and == checks for reference equality by default, the references of a and b are compared, as both are objects. If you want to compare value equality, you have to unbox the ints first.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • This is throwing me off, too. Since [`Equals` is `virtual`](http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx) and `a` and `b` are both really `int`, I expected it to call `Int32.Equals`. Maybe I'm misunderstanding how `virtual` works in C#? – Powerlord Nov 18 '10 at 14:58
  • `virtual` does not work for static functions. You are calling `object.equals(a, b)`, which is something different than `a.equals(b)`. – Heinzi Nov 18 '10 at 14:59
  • @Heinzi: I'm referring to the `a == b`, which is the one returning the wrong value. – Powerlord Nov 18 '10 at 15:01
  • I get that a and b are boxed - I did this on purpose. static object Equals delegates to Int32.Equals, which makes sense. I also expected operator == to do the same given the quote. – MrNick Nov 18 '10 at 15:02
  • `a` and `b` are both objects, that's why the `object.Equals(object)` method is getting called, which returns reference equality, not value equality. – Femaref Nov 18 '10 at 15:02
  • 1
    @R.Bemrose: Ah, I see. Well, virtual does not work for operator overloading either, because it's implemented as a static method. – Heinzi Nov 18 '10 at 15:03
  • 1
    This answer is WRONG. The default `==` does NOT call `Object.Equals`. If it did, the issue the OP is asking about wouldn't be occurring. – jason Nov 18 '10 at 15:04
  • @Heinzi: That would explain it. – Powerlord Nov 18 '10 at 15:04
  • I refer you to §7.10.6 of the specification and encourage you to read it very carefully. – jason Nov 18 '10 at 15:09
  • I can see that a == b is testing REFERENCE equality, but my concern was that this is at odds with what the book says (or my understanding at least). – MrNick Nov 18 '10 at 15:11
  • @MrNick: The book is wrong (or the appropriate context wasn't given). Please see my answer (http://stackoverflow.com/questions/4216023/c-system-object-operator/4216092#4216092). – jason Nov 18 '10 at 15:14
1

When two objects are tested for equality they are tested to see if they are referencing the same object. (EDIT: this is generally true, however == could be overloaded to provide the functionality that you receive from a.equals)

So

object a = 1;  
object b = 1;

These do not point to the same address space.

However if you did

object a = 1;
object b = a;

Then these would point to the same address.

For a real life example, take two different apartments in the same building, they have the exact same layout, same 1 bedroom, same kitchen, same paint everything about them is the same, except that apartment a is #101 and apartment b is #102. In one sense they are the same a.equals(b), but in another sense they are completely different a != b.

Nathan Koop
  • 24,803
  • 25
  • 90
  • 125
0

== implementation of object checks for identity, not equality. You have two variables that point to two different objects that's why == returns false.

Andrew Bezzub
  • 15,744
  • 7
  • 51
  • 73
0

You declared a and b as object which is a reference type and not a value type. So with a==b you are comparing references of objects (which will be different) rather than the values.

Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
0

System.Object does not overload ==, so a == b just tests for reference equality (and returns false). Since operator overloading is implemented as a static method, it's not virtual.

Object.Equals, on the other side, is specified as follows:

The default implementation of Equals supports reference equality for reference types, and bitwise equality for value types. Reference equality means the object references that are compared refer to the same object. Bitwise equality means the objects that are compared have the same binary representation.

Since a and b have the same binary representation, Object.Equals(a, b) returns true.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
0

System.Object.operator==() will call a.Equals(b) to determine if a and b are equal

This is simply not true. If that were the case then you'd have a == b returning true, since a.Equals(b) returns true. Equals is a virtual method, so it doesn't matter that int values are boxed; if you call a.Equals, that compiles to a callvirt and the vtable is used.

So the static == operator does not use a.Equals(b) internally. It tests for reference equality by default. It only does otherwise if the static == operator has been overloaded for the types in the expression as they are declared at compile time.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • This is what I was trying to understand. I can see from my test that a == b is testing reference equality (and I'm fine with that), but the quote from the book makes it sound as though it should have the same behaviour as static object.Equals(object, object). So can I conclude that the book is incorrect? Maybe older versions of .Net were different? – MrNick Nov 18 '10 at 15:09
  • @MrNick: Right, the book is incorrect. I don't know if older versions of .NET were different; but honestly, I **highly** doubt it. MS is typically quite cautious about introducing breaking changes, and that would be one. In fact I've never come across a change in .NET that would lead to backwards compatibility issues. But that's just me. – Dan Tao Nov 18 '10 at 15:17