== vs. Object.Equals(object) in .NET

.Net

.Net Problem Overview


So, when I was a comparative novice to the novice I am right now, I used to think that these two things were syntactic sugar for each other, i.e. that using one over the other was simply a personal preference. Over time, I'm come to find that these two are not the same thing, even in a default implementation (see this and this). To further confuse the matter, each can be overridden/overloaded separately to have completely different meanings.

Is this a good thing, what are the differences, and when/why should you use one over the other?

.Net Solutions


Solution 1 - .Net

string x = "hello";
string y = String.Copy(x);
string z = "hello";

To test if x points to the same object as y:

(object)x == (object)y  // false
x.ReferenceEquals(y)    // false
x.ReferenceEquals(z)    // true (because x and z are both constants they
                        //       will point to the same location in memory)

To test if x has the same string value as y:

x == y        // true
x == z        // true
x.Equals(y)   // true
y == "hello"  // true

Note that this is different to Java. In Java the == operator is not overloaded so a common mistake in Java is:

y == "hello"  // false (y is not the same object as "hello")

For string comparison in Java you need to always use .equals()

y.equals("hello")  // true

Solution 2 - .Net

MSDN has clear and solid descriptions of both things.

object.Equals method

operator ==

Overloadable Operators

Guidelines for Overriding Equals() and Operator ==

> Is this a good thing, what are the > differences, and when/why should you > use one over the other?

How can it be "good" or "bad" thing? One - method, another - operator. If reference equality is not sufficient, overload them, otherwise leave them as is. For primitive types they just work out of box.

Solution 3 - .Net

Microsoft says that class implementers should make == behave as similarly as possible to Equals:

> DO ensure that Object.Equals and the equality operators have exactly the same semantics

from http://msdn.microsoft.com/en-us/library/vstudio/7h9bszxx(v=vs.110).aspx


If you want to be certain you are getting IDENTITY comparison (when comparing references), then use ReferenceEquals instead.

If a class implementor does not override ==, then static method is looked for, at compile time, in base classes. If this search reaches Object, then Object.== is used. For classes, this is same as ReferenceEquals.

If class documentation is uncertain as to whether a given class (from a vendor other than Microsoft presumably) implements == as Equals or ReferenceEquals (or it could in theory be different than both of those), I sometimes avoid ==. Instead, I use the less readable Equals(a, b) or ReferenceEquals(a, b), depending on which meaning I want.

OTOH, ps2goat makes a good point that using == avoids exception if first operand is null (because == is a static operator). This is an argument in favor of using ==.


Removed controversial commentary regarding ==


UPDATE A recent Microsoft doc quote, from .Net 4.7.2 retrieved Feb. 2019, shows they still intend the two to behave similarly:

Object.Equals Method

> Some languages such as C# and Visual Basic support operator overloading. When a type overloads the equality operator, it must also override the Equals(Object) method to provide the same functionality. This is typically accomplished by writing the Equals(Object) method in terms of the overloaded equality operator, as in the following example.


NOTE: See other answers for the consequences of == being a static method vs Equals being an instance method. I'm not claiming behavior is identical; I'm observing that Microsoft recommends making the two as similar as possible.

Solution 4 - .Net

I was going to post this as a comment on the accepted answer, but I think this deserves to be considered when determining which route to take.

dotnetfiddle: https://dotnetfiddle.net/gESLzO

Fiddle code:

    Object a = null;
	Object b = new Object();
	
    // Ex 1
	Console.WriteLine(a == b);
    // Ex 2
	Console.WriteLine(b == a);

    // Ex 3		
	Console.WriteLine(b.Equals(a));
    // Ex 4
	Console.WriteLine(a.Equals(b));

The first 3 WriteLine examples will work, but the fourth throws an exception. 1 and 2 use ==, which is a static method that does not require either object to be instantiated.

Example 3 works because b is instantiated.

Example 4 fails because a is null, and thus a method can not be called on a null object.

Because I try to code as lazily as possible, I use ==, especially when working with scenarios where either object (or both) can be null. If I didn't, I'd have to do a null check, first, before being able to call .Equals().

Solution 5 - .Net

My understanding of the uses of both was this: use == for conceptual equality (in context, do these two arguments mean the same thing?), and .Equals for concrete equality (are these two arguments in actual fact the exact same object?).

Edit: Kevin Sheffield's linked article does a better job of explaining value vs. reference equality…

Solution 6 - .Net

To answer this, we must describe the four kinds of object equivalence:

  1. Reference Equality, object.ReferenceEquals(a, b): The two variables point to the same exact object in RAM. (If this were C, both variables would have the same exact pointer.)

  2. Interchangeability, a == b: The two variables refer to objects that are completely interchangeable. Thus, when a == b, Func(a,b) and Func(b,a) do the same thing.

  3. Semantic Equality, object.Equals(a, b): At this exact moment in time, the two objects mean the same thing.

  4. Entity equality, a.Id == b.Id: The two objects refer to the same entity, such as a database row, but don’t have to have the same contents.

As a programmer, when working with an object of a known type, you need to understand the kind of equivalence that’s appropriate for your business logic at the specific moment of code that you’re in.

The simplest example about this is the string versus StringBuilder types. String overrides ==, StringBuilder does not:

var aaa1 = "aaa";
var aaa2 = $"{'a'}{'a'}{'a'}";
var bbb = "bbb";

// False because aaa1 and aaa2 are completely different objects with different locations in RAM
Console.WriteLine($"Object.ReferenceEquals(aaa1, aaa2): {Object.ReferenceEquals(aaa1, aaa2)}");

// True because aaa1 and aaa2 are completely interchangable
Console.WriteLine($"aaa1 == aaa2: {aaa1 == aaa2}");             // True
Console.WriteLine($"aaa1.Equals(aaa2): {aaa1.Equals(aaa2)}");   // True
Console.WriteLine($"aaa1 == bbb: {aaa1 == bbb}");               // False
Console.WriteLine($"aaa1.Equals(bbb): {aaa1.Equals(bbb)}");     // False

// Won't compile
// This is why string can override ==, you can not modify a string object once it is allocated
//aaa1[0] = 'd';

// aaaUpdated and aaa1 point to the same exact object in RAM
var aaaUpdated = aaa1;
Console.WriteLine($"Object.ReferenceEquals(aaa1, aaaUpdated): {Object.ReferenceEquals(aaa1, aaaUpdated)}"); // True

// aaaUpdated is a new string, aaa1 is unmodified
aaaUpdated += 'c';
Console.WriteLine($"Object.ReferenceEquals(aaa1, aaaUpdated): {Object.ReferenceEquals(aaa1, aaaUpdated)}"); // False

var aaaBuilder1 = new StringBuilder("aaa");
var aaaBuilder2 = new StringBuilder("aaa");

// False, because both string builders are different objects
Console.WriteLine($"Object.ReferenceEquals(aaaBuider1, aaaBuider2): {Object.ReferenceEquals(aaa1, aaa2)}");

// Even though both string builders have the same contents, they are not interchangable
// Thus, == is false
Console.WriteLine($"aaaBuider1 == aaaBuilder2: {aaaBuilder1 == aaaBuilder2}");

// But, because they both have "aaa" at this exact moment in time, Equals returns true
Console.WriteLine($"aaaBuider1.Equals(aaaBuilder2): {aaaBuilder1.Equals(aaaBuilder2)}");

// Modifying the contents of the string builders changes the strings, and thus
// Equals returns false
aaaBuilder1.Append('e');
aaaBuilder2.Append('f');
Console.WriteLine($"aaaBuider1.Equals(aaaBuilder2): {aaaBuilder1.Equals(aaaBuilder2)}");

To get into more details, we can work backwards, starting with entity equality. In the case of entity equality, properties of the entity may change over time, but the entity’s primary key never changes. This can be demonstrated with pseudocode:

// Hold the current user object in a variable
var originalUser = database.GetUser(123);

// Update the user’s name
database.UpdateUserName(123, user.Name + "son");

var updatedUser = database.GetUser(123);

Console.WriteLine(originalUser.Id == updatedUser.Id); // True, both objects refer to the same entity
Console.WriteLine(Object.Equals(originalUser, updatedUser); // False, the name property is different

Moving to semantic equality, the example changes slightly:

var originalUser = new User() { Name = "George" };
var updatedUser = new User() { Name = "George" };

Console.WriteLine(Object.Equals(originalUser, updatedUser); // True, the objects have the same contents
Console.WriteLine(originalUser == updatedUser); // User doesn’t define ==, False

updatedUser.Name = "Paul";

Console.WriteLine(Object.Equals(originalUser, updatedUser); // False, the name property is different

What about interchangeability? (overriding ==) That’s more complicated. Let’s build on the above example a bit:

var originalUser = new User() { Name = "George" };
var updatedUser = new User() { Name = "George" };
Console.WriteLine(Object.Equals(originalUser, updatedUser); // True, the objects have the same contents

// Does this change updatedUser? We don’t know
DoSomethingWith(updatedUser);

// Are the following equivalent?
// SomeMethod(originalUser, updatedUser);
// SomeMethod(updatedUser, originalUser);

In the above example, DoSomethingWithUser(updatedUser) might change updatedUser. Thus we can no longer guarantee that the originalUser and updatedUser objects are "Equals." This is why User does not override ==.

A good example for when to override == is with immutable objects. An immutable object is an object who’s publicly-visible state (properties) never change. The entire visible state must be set in the object’s constructor. (Thus, all properties are read-only.)

var originalImmutableUser = new ImmutableUser(name: "George");
var secondImmutableUser = new ImmutableUser(name: "George");

Console.WriteLine(Object.Equals(originalImmutableUser, secondImmutableUser); // True, the objects have the same contents
Console.WriteLine(originalImmutableUser == secondImmutableUser); // ImmutableUser defines ==, True

// Won’t compile because ImmutableUser has no setters
secondImmutableUser.Name = "Paul";

// But this does compile
var updatedImmutableUser = secondImmutableUser.SetName("Paul"); // Returns a copy of secondImmutableUser with Name changed to Paul.

Console.WriteLine(object.ReferenceEquals(updatedImmutableUser, secondImmutableUser)); // False, because updatedImmutableUser is a different object in a different location in RAM

// These two calls are equivalent because the internal state of an ImmutableUser can never change
DoSomethingWith(originalImmutableUser, secondImmutableUser);
DoSomethingWith(secondImmutableUser, originalImmutableUser);

Should you override == with a mutable object? (That is, an object who’s internal state can change?) Probably not. You would need to build a rather complicated event system to maintain interchangeability.

In general, I work with a lot of code that uses immutable objects, so I override == because it’s more readable than object.Equals. When I work with mutable objects, I don’t override == and rely on object.Equals. Its the programmer’s responsibility to know if the objects they are working with are mutable or not, because knowing if something’s state can change should influence how you design your code.

The default implementation of == is object.ReferenceEquals because, with mutable objects, interchangeability is only guaranteed when the variables point to the same exact object in RAM. Even if the objects have the same contents at a given point in time, (Equals returns true,) there is no guarantee that the objects will continue to be equal; thus the objects are not interchangeable. Thus, when working with a mutable object that does not override ==, the default implementation of == works, because if a == b, they are the same object, and SomeFunc(a, b) and SomeFunc(b, a) are exactly the same.

Furthermore, if a class does not define equivalence, (For example, think of a database connection, and open file handle, ect,) then the default implementation of == and Equals fall back to reference equality, because two variables of type database connection, open file handle, ect, are only equal if they are the exact instance of the database connection, open file handle, ect. Entity equality might make sense in business logic that needs to know that two different database connections refer to the same database, or that two different file handles refer to the same file on disk.

Now, for my soapbox moment. In my opinion, C# handles this topic in a confusing way. == should be for semantic equality, instead of the Equals method. There should be a different operator, like ===, for interchangeability, and potentially another operator, ====, for referential equality. This way, someone who's a novice, and / or writing CRUD applications, only needs to understand ==, and not the more nuanced details of interchangeability and referential equality.

Solution 7 - .Net

You may want to use .Equals as someone may come along at a later time and overload them for you class.

Solution 8 - .Net

Two of the most often used types, String and Int32, implement both operator==() and Equals() as value equality (instead of reference equality). I think one can consider these two defining examples, so my conclusion is that both have identical meanings. If Microsoft states otherwise, I think they are intentionally causing confusion.

Solution 9 - .Net

Operator == and Equals() both are same while we are comparing values instead of references. Output of both are same see below example.

Example

    static void Main()
    {
        string x = " hello";
        string y = " hello";
        string z = string.Copy(x);
        if (x == y)
        {
            Console.WriteLine("== Operator");
        }
        if(x.Equals(y))
        {
            Console.WriteLine("Equals() Function Call");
        }
        if (x == z)
        {
            Console.WriteLine("== Operator while coping a string to another.");
        }
        if (x.Equals(y))
        {
            Console.WriteLine("Equals() Function Call while coping a string to another.");
        }
    }

Output:

  == Operator
  Equals() Function Call
  == Operator while coping a string to another.
  Equals() Function Call while coping a string to another.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthew ScharleyView Question on Stackoverflow
Solution 1 - .NetmolassesView Answer on Stackoverflow
Solution 2 - .NetakuView Answer on Stackoverflow
Solution 3 - .NetToolmakerSteveView Answer on Stackoverflow
Solution 4 - .Netps2goatView Answer on Stackoverflow
Solution 5 - .NetandrewdotnichView Answer on Stackoverflow
Solution 6 - .NetAndrew RondeauView Answer on Stackoverflow
Solution 7 - .NetEd S.View Answer on Stackoverflow
Solution 8 - .NetDimitri C.View Answer on Stackoverflow
Solution 9 - .NetSonu RajpootView Answer on Stackoverflow