101

My understanding of these three was:

  • .Equals() tests for data equality (for the lack of a better description). .Equals() can return True for different instances of the same object, and this is the most commonly overridden method.

  • .ReferenceEquals() tests whether or not two objects are the same instance and cannot be overridden.

  • == is the same as the ReferenceEquals() by default, but this CAN be overridden.

But C# station states:

In the object class, the Equals and ReferenceEquals methods are semantically equivalent, except that the ReferenceEquals works only on object instances. The ReferenceEquals method is static.

Now I don't get it. Can anyone shed some light on this?

999999
  • 1,873
  • 3
  • 14
  • 20
  • See http://stackoverflow.com/questions/814878/c-difference-between-and-equals and many other StackOverflow questions on this topic. – Ian Mercer Oct 06 '10 at 05:13
  • @High I have. It's just the part I extracted from C# Station that's confusing me. – 999999 Oct 06 '10 at 05:16
  • 1
    check this: [Guidelines for Overriding Equals() and Operator == (C# Programming Guide)](http://msdn.microsoft.com/en-us/library/ms173147.aspx) – pinichi Oct 06 '10 at 05:07

7 Answers7

105

The source of your confusion appears to be that there is a typo in the extract from C# station, which should read: "... except that the Equals works only on object instances. The ReferenceEquals method is static."


You are loosely correct about the differences in the semantic meanings of each (although "different instances of the same object" seems a little confused, it should probably read "different instances of the same type) and about which can be overridden.

If we leave that aside, let's deal with the last bit of your question, i.e. how they work with plainSystem.Objectinstances and System.Objectreferences (we need both to dodge the non-polymorphic nature of ==). Here, all three operations will work equivalentally, but with a caveat:Equalscannot be invoked onnull.

Equalsis an instance method that takes one parameter (which can benull). Since it is an instance method (must be invoked on an actual object), it can't be invoked on a null-reference.

ReferenceEquals is a static method that takes two parameters, either / both of which can be null. Since it is static (not associated with an object instance), it will not throw aNullReferenceException under any circumstances.

==is an operator, that, in this case (object), behaves identically to ReferenceEquals. It will not throw aNullReferenceExceptioneither.

To illustrate:

object o1 = null;
object o2 = new object();

//Technically, these should read object.ReferenceEquals for clarity, but this is redundant.
ReferenceEquals(o1, o1); //true
ReferenceEquals(o1, o2); //false
ReferenceEquals(o2, o1); //false
ReferenceEquals(o2, o2); //true

o1.Equals(o1); //NullReferenceException
o1.Equals(o2); //NullReferenceException
o2.Equals(o1); //false
o2.Equals(o2); //true
RajeshKdev
  • 6,365
  • 6
  • 58
  • 80
Ani
  • 111,048
  • 26
  • 262
  • 307
  • So is the excerpt from C# station quoted above wrong (especially if I override `.Equals()`)? – 999999 Oct 06 '10 at 05:17
  • 1
    The excerpt states **"in the `object` class"**. I think you skipped over that part? Because otherwise you would not be talking about overriding it. – Domenic Oct 06 '10 at 05:34
  • 2
    My answer is *only* about the `object` class. – Ani Oct 06 '10 at 05:37
  • @Ani: your below sentence was wrong static method can throw NullReferenceException: Since it is static (not associated with an object instance), it will not throw aNullReferenceException under any circumstances. – selvaraj Oct 06 '10 at 07:15
  • @saj: To clarify, the sentence does not mean that static methods in general cannot throw NullReferenceExceptions. The "circumstances" that I refer to here are whether either of the references to be compared are `null`. Apologies, I thought that was implied. – Ani Oct 06 '10 at 07:20
  • 2
    `Equals` is also a static method on `object` which takes two parameters. One or both can then be `null`. – weston Nov 30 '12 at 15:20
  • @weston This is not right, `Equals` takes only one parameter, the other value is taken as `this` - the object which invokes it. If you invoke `obj.Equals(obj2)`, when `obj == null`, you'll get an exception, since `.Equals` is a method of an `Object` class, and can't be invoked on null – Phoera May 28 '21 at 19:45
  • @Phoera I do not understand what you are correcting. I already said "Since it is an instance method (must be invoked on an actual object), it can't be invoked on a null-reference." – weston May 31 '21 at 03:24
  • @weston You wrote "Equals is also a static method on object which takes two parameters." - it takes one. You also wrote "One or both can then be null" - only 1 can be null, the parameter. The object you call it on can't be null. I was only referring to this comment. – Phoera Jun 07 '21 at 07:27
  • 1
    @Phoera I see what you are referring to now, but as I said, it is _also_ a static that takes two parameters: https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0#System_Object_Equals_System_Object_System_Object_ e.g. `Object.Equals(a, b)` – weston Jun 07 '21 at 14:11
  • My understanding is that it is possible to have equality without reference equality. Is it possible to have reference equality without equality? The latter seems completely impossible but I would like to test my thinking – Dean P Jan 16 '22 at 10:08
27

Have a look at this MSDN article on the subject.

I think the pertinent points are:

To check for reference equality, use ReferenceEquals. To check for value equality, use Equals or Equals.

By default, the operator == tests for reference equality by determining if two references indicate the same object, so reference types do not need to implement operator == in order to gain this functionality. When a type is immutable, meaning the data contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value.

Hope this helps!

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
10

Your understanding of .ReferenceEquals is correct.

.Equals checks data equality for value types, and reference equality for non-value types (general objects).

.Equals can be overridden for objects to perform some form of data equality check

EDIT: Also, .ReferenceEquals can't be used on value types (well it can, but will always be false)

Luke Schafer
  • 9,209
  • 2
  • 28
  • 29
3

Want to add my five cents about comparing with "null".

  1. ReferenceEquals(object, object) is the same as "(object)arg1 == arg2" (so in case of value types, you get boxing and it takes time). But this method is the only 100% safe way to check your argument for null in several situations, like

    • a) before calling it's members via . operator
    • b) checking the result of AS operator.
  2. == and Equals(). Why i'm saying that ReferenceEquals is 100% safe with null-checkings? Imagine you write generic extensions in core cross-project libs, and lets say you restrict generic parameter type to some domain type. This type can introduce "==" operator -- now or later (and believe me, I've seen much, this operator can have a very "strange" logic, especially if it comes to domain or persistence objects). You try to check you argument for null and then call member operation on it. Surprise, you CAN have NullRef here. Because == operator is almost the same as Equals() - very custom and very unpredictable. There is a difference though, which should be taken into account - if you don't restrict your generic parameter to some custom type (== can be used only if your type is "class"), == operator is the same as object.ReferenceEquals(..). Equals implementation is always used from final type, as it's virtual.

So my recommendation is, when you write your own types or derive from well-known types, you can use == to check for null. Otherwise use object.ReferenceEquals(arg, null).

sotonika
  • 161
  • 1
  • 5
2

I've expanded on Ani's excellent answer to show the key differences when dealing with reference types and overridden equality methods.

.

void Main()
{

    //odd os are null; evens are not null
    object o1 = null;
    object o2 = new object();
    object o3 = null;
    object o4 = new object();
    object o5 = o1;
    object o6 = o2;

    Demo d1 = new Demo(Guid.Empty);
    Demo d2 = new Demo(Guid.NewGuid());
    Demo d3 = new Demo(Guid.Empty);

    Debug.WriteLine("comparing null with null always yields true...");
    ShowResult("ReferenceEquals(o1, o1)", () => ReferenceEquals(o1, o1)); //true
    ShowResult("ReferenceEquals(o3, o1)", () => ReferenceEquals(o3, o1)); //true
    ShowResult("ReferenceEquals(o5, o1)", () => ReferenceEquals(o5, o1)); //true 
    ShowResult("o1 == o1", () => o1 == o1); //true
    ShowResult("o3 == o1", () => o3 == o1); //true
    ShowResult("o5 == o1", () => o5 == o1); //true 

    Debug.WriteLine("...though because the object's null, we can't call methods on the object (i.e. we'd get a null reference exception).");
    ShowResult("o1.Equals(o1)", () => o1.Equals(o1)); //NullReferenceException
    ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
    ShowResult("o3.Equals(o1)", () => o3.Equals(o1)); //NullReferenceException
    ShowResult("o3.Equals(o2)", () => o3.Equals(o2)); //NullReferenceException
    ShowResult("o5.Equals(o1)", () => o5.Equals(o1));  //NullReferenceException
    ShowResult("o5.Equals(o2)", () => o5.Equals(o1));  //NullReferenceException

    Debug.WriteLine("Comparing a null object with a non null object always yeilds false");
    ShowResult("ReferenceEquals(o1, o2)", () => ReferenceEquals(o1, o2)); //false
    ShowResult("ReferenceEquals(o2, o1)", () => ReferenceEquals(o2, o1)); //false
    ShowResult("ReferenceEquals(o3, o2)", () => ReferenceEquals(o3, o2)); //false
    ShowResult("ReferenceEquals(o4, o1)", () => ReferenceEquals(o4, o1)); //false
    ShowResult("ReferenceEquals(o5, o2)", () => ReferenceEquals(o3, o2)); //false
    ShowResult("ReferenceEquals(o6, o1)", () => ReferenceEquals(o4, o1)); //false
    ShowResult("o1 == o2)", () => o1 == o2); //false
    ShowResult("o2 == o1)", () => o2 == o1); //false
    ShowResult("o3 == o2)", () => o3 == o2); //false
    ShowResult("o4 == o1)", () => o4 == o1); //false
    ShowResult("o5 == o2)", () => o3 == o2); //false
    ShowResult("o6 == o1)", () => o4 == o1); //false
    ShowResult("o2.Equals(o1)", () => o2.Equals(o1)); //false
    ShowResult("o4.Equals(o1)", () => o4.Equals(o1)); //false
    ShowResult("o6.Equals(o1)", () => o4.Equals(o1)); //false

    Debug.WriteLine("(though again, we can't call methods on a null object:");
    ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
    ShowResult("o1.Equals(o4)", () => o1.Equals(o4)); //NullReferenceException
    ShowResult("o1.Equals(o6)", () => o1.Equals(o6)); //NullReferenceException

    Debug.WriteLine("Comparing 2 references to the same object always yields true");
    ShowResult("ReferenceEquals(o2, o2)", () => ReferenceEquals(o2, o2)); //true    
    ShowResult("ReferenceEquals(o6, o2)", () => ReferenceEquals(o6, o2)); //true <-- Interesting
    ShowResult("o2 == o2", () => o2 == o2); //true  
    ShowResult("o6 == o2", () => o6 == o2); //true <-- Interesting
    ShowResult("o2.Equals(o2)", () => o2.Equals(o2)); //true 
    ShowResult("o6.Equals(o2)", () => o6.Equals(o2)); //true <-- Interesting

    Debug.WriteLine("However, comparing 2 objects may yield false even if those objects have the same values, if those objects reside in different address spaces (i.e. they're references to different objects, even if the values are similar)");
    Debug.WriteLine("NB: This is an important difference between Reference Types and Value Types.");
    ShowResult("ReferenceEquals(o4, o2)", () => ReferenceEquals(o4, o2)); //false <-- Interesting
    ShowResult("o4 == o2", () => o4 == o2); //false <-- Interesting
    ShowResult("o4.Equals(o2)", () => o4.Equals(o2)); //false <-- Interesting

    Debug.WriteLine("We can override the object's equality operator though, in which case we define what's considered equal");
    Debug.WriteLine("e.g. these objects have different ids, so we treat as not equal");
    ShowResult("ReferenceEquals(d1,d2)",()=>ReferenceEquals(d1,d2)); //false
    ShowResult("ReferenceEquals(d2,d1)",()=>ReferenceEquals(d2,d1)); //false
    ShowResult("d1 == d2",()=>d1 == d2); //false
    ShowResult("d2 == d1",()=>d2 == d1); //false
    ShowResult("d1.Equals(d2)",()=>d1.Equals(d2)); //false
    ShowResult("d2.Equals(d1)",()=>d2.Equals(d1)); //false
    Debug.WriteLine("...whilst these are different objects with the same id; so we treat as equal when using the overridden Equals method...");
    ShowResult("d1.Equals(d3)",()=>d1.Equals(d3)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect)
    ShowResult("d3.Equals(d1)",()=>d3.Equals(d1)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect)
    Debug.WriteLine("...but as different when using the other equality tests.");
    ShowResult("ReferenceEquals(d1,d3)",()=>ReferenceEquals(d1,d3)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("ReferenceEquals(d3,d1)",()=>ReferenceEquals(d3,d1)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("d1 == d3",()=>d1 == d3); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("d3 == d1",()=>d3 == d1); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)


    Debug.WriteLine("For completeness, here's an example of overriding the == operator (wihtout overriding the Equals method; though in reality if overriding == you'd probably want to override Equals too).");
    Demo2 d2a = new Demo2(Guid.Empty);
    Demo2 d2b = new Demo2(Guid.NewGuid());
    Demo2 d2c = new Demo2(Guid.Empty);
    ShowResult("d2a == d2a", () => d2a == d2a); //true
    ShowResult("d2b == d2a", () => d2b == d2a); //false
    ShowResult("d2c == d2a", () => d2c == d2a); //true <-- interesting
    ShowResult("d2a != d2a", () => d2a != d2a); //false
    ShowResult("d2b != d2a", () => d2b != d2a); //true
    ShowResult("d2c != d2a", () => d2c != d2a); //false <-- interesting
    ShowResult("ReferenceEquals(d2a,d2a)", () => ReferenceEquals(d2a, d2a)); //true
    ShowResult("ReferenceEquals(d2b,d2a)", () => ReferenceEquals(d2b, d2a)); //false
    ShowResult("ReferenceEquals(d2c,d2a)", () => ReferenceEquals(d2c, d2a)); //false <-- interesting
    ShowResult("d2a.Equals(d2a)", () => d2a.Equals(d2a)); //true
    ShowResult("d2b.Equals(d2a)", () => d2b.Equals(d2a)); //false
    ShowResult("d2c.Equals(d2a)", () => d2c.Equals(d2a)); //false <-- interesting   

}



//this code's just used to help show the output in a friendly manner
public delegate bool Statement();
void ShowResult(string statementText, Statement statement)
{
    try 
    {
        Debug.WriteLine("\t{0} => {1}",statementText, statement());
    }
    catch(Exception e)
    {
        Debug.WriteLine("\t{0} => throws {1}",statementText, e.GetType());
    }
}

class Demo
{
    Guid id;
    public Demo(Guid id) { this.id = id; }
    public override bool Equals(object obj)
    {
        return Equals(obj as Demo); //if objects are of non-comparable types, obj will be converted to null
    }
    public bool Equals(Demo obj)
    {
        if (obj == null)
        {
            return false;
        }
        else
        {
            return id.Equals(obj.id);
        }
    }
    //if two objects are Equal their hashcodes must be equal
    //however, if two objects hash codes are equal it is not necessarily true that the objects are equal
    //i.e. equal objects are a subset of equal hashcodes
    //more info here: https://stackoverflow.com/a/371348/361842
    public override int GetHashCode()
    {
        return id.GetHashCode();
    }
}

class Demo2
{
    Guid id;
    public Demo2(Guid id)
    {
        this.id = id;
    }

    public static bool operator ==(Demo2 obj1, Demo2 obj2)
    {
        if (ReferenceEquals(null, obj1)) 
        {
            return ReferenceEquals(null, obj2); //true if both are null; false if only obj1 is null
        }
        else
        {
            if(ReferenceEquals(null, obj2)) 
            {
                return false; //obj1 is not null, obj2 is; therefore false
            }
            else
            {
                return obj1.id == obj2.id; //return true if IDs are the same; else return false
            }
        }
    }

    // NB: We also HAVE to override this as below if overriding the == operator; this is enforced by the compiler.  However, oddly we could choose to override it different to the below; but typically that would be a bad idea...
    public static bool operator !=(Demo2 obj1, Demo2 obj2)
    {
        return !(obj1 == obj2);
    }
}
Community
  • 1
  • 1
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
1

In Object class .Equals implements identity, not equality. It checks if references are equal. The code could be like this:

public virtual Boolean Equals(Object other) {
    if (this == other) return true;
    return false;
}

While implementing .Equals in your class you should call base class .Equals only if the base class is not Object. Yeh, that is complicated.

Even more, as derived classes can override .Equals and so you can't call it to check identity Microsoft added static .ReferenceEquals method.

If you use some class then for you logically .Equals checks for equality and .ReferenceEquals checks for identity.

Yola
  • 18,496
  • 11
  • 65
  • 106
-4

Equals() checks for hash code or equivalence depending on the underlying type(Value/Reference) and ReferenceEquals() is intended to always check for hash code. ReferenceEquals returns true if both objects point to same memory location.

double e = 1.5;
double d = e;
object o1 = d;
object o2 = d;

Console.WriteLine(o1.Equals(o2)); // True
Console.WriteLine(Object.Equals(o1, o2)); // True
Console.WriteLine(Object.ReferenceEquals(o1, o2)); // False

Console.WriteLine(e.Equals(d)); // True
Console.WriteLine(Object.Equals(e, d)); // True
Console.WriteLine(Object.ReferenceEquals(e, d)); // False
Sebastian Hofmann
  • 1,440
  • 6
  • 15
  • 21
Atulya
  • 153
  • 1
  • 11
  • 3
    This is nonsense. Neither Equals nor ReferenceEquals looks at the HashCode. There's simply a requirement that HashCodes of Equals objects must be equal. And objects don't point anywhere ... ReferenceEquals is true if and only if both of its arguments are the same reference object or both are null. – Jim Balter Dec 24 '17 at 04:10