What is the difference in using the Equals method for the = = operator?

What is the difference in using the Equals method for the == operator in situations of comparing (1) Value Types and (2) reference types?

Author: Maniero, 2014-06-02

4 answers

According to Microsoft in text written by Jon Skeet :

The Equals method is just a virtual method defined in the System.Object, and it can be superimposed by any classes that choose to do so. the == is an operator that can be overloaded by classes, when it normally has identity behavior.

For reference types, where == has not been overloaded, it compares if two references refer to the same object, which is exactly what the Equals implementation does in System.Object.

Value types do not provide an overhead for == by default. However, most types of values provided by .NET provide their own overhead. The standard implementation of Equals() for a value type is provided by ValueType, and uses reflection to make the comparison, which makes it significantly slower than a specific implementation for the type would normally be. This implementation also calls Equals() in reference pairs within the two values being compared.

However, the main difference between the two types of comparison under normal conditions of use (where you are unlikely to define your own value types) is polymorphism. Operators are overloaded, not overlapped ( override ), which means that unless the compiler knows it should call the most specific version, it will only call the version identity. To illustrate this, here is an example:

using static System.Console;
                    
public class Program {
    public static void Main() {
        // cria duas variáveis iguais mas distintas uma da outra
        string a = new string(new char[] {'h', 'e', 'l', 'l', 'o'});
        string b = new string(new char[] {'h', 'e', 'l', 'l', 'o'});
        WriteLine (a == b);
        WriteLine (a.Equals(b));
        // o mesmo teste usando os mesmo dados mas como variáveis do tipo `Object`
        object c = a;
        object d = b;
        WriteLine (c == d);
        WriteLine (c.Equals(d));
    }
}

The result is:

True
True
False
True

The third line is false because the compiler can only call the non-overloaded version of == since it does not know that the contents of c and d are the two references of strings. Since they are references to different strings, the identity operator returns false.

So when should you use the operator? The basic rule is that for almost all types of reference, use is equal to when you want to test equality instead of reference identity. The exception is for strings - comparing strings with == makes things much simpler and more readable, but you have to remember that both sides of the operator must be expressions of type string, to get that comparison to work properly.

For value types, == is usually used for better readability. It gets more complicated if a guy of value, it provides an overhead for == that acts differently for Equals, but this would be such a very poorly designed situation.


Summing it all up:

You choose what each one should do according to the programmer's intuitive expectation.

What does the programmer expect to compare when he uses a ==? This is what this comparison should do, no matter how it is done. It can compare its members or references. It should compare to identity .

Already the method Equals expect the comparison to be based on the value of Type . That is, based on its value, on the members relevant to the type.

It is good to remember that there is a method ReferenceEquals() which has the function of specifically comparing the references of objects .

It is common for the == operator to choose to use the Equals() implementation or the ReferenceEquals() implementation.

Operator == is static and works more as if it were an extension method.

There are cases where Equals() has three possible versions for compiler choice:

  • or static method of object (Equals(object, object));
  • the virtual method coming from object (Equals(object));
  • the method that implements IEquatable<short> (Equals(short) ).

So inconsistencies can occur.

Inconsistencies

int myInt = 1;
short myShort = 1;
object objInt1 = myInt;
object objInt2 = myInt;
object objShort = myShort;
WriteLine(myInt == myShort);          // cenário 1 true
WriteLine(myShort == myInt);          // cenário 2 true
WriteLine(myInt.Equals(myShort));     // cenário 3 true
WriteLine(myShort.Equals(myInt));     // cenário 4 false!
WriteLine(objInt1 == objInt1);        // cenário 5 true
WriteLine(objInt1 == objShort);       // cenário 6 false!!
WriteLine(objInt1 == objInt2);        // cenário 7 false!!!
WriteLine(Equals(objInt1, objInt2));  // cenário 8 true
WriteLine(Equals(objInt1, objShort)); // cenário 9 false!?!

Strings

This is a type that exemplifies as well as semantics is more important that the linearity of operation. See examples:

string s1 = "abc";
string s2 = "abc";
WriteLine(object.ReferenceEquals(s1, s2)); //retorna true

This occurs because of a flame techniqueinterning which attempts to reuse an existing allocation of a string identical to the one that attempts to reallocate. But this is an exception. See now:

string s3 = "abc";
string s4t = "ab";
string s4 = s4t + "c";
WriteLine(object.ReferenceEquals(s3, s4)); //retorna false
WriteLine(s3 == s4); //retorna true

If the == operator adopted reference comparison as it usually does with reference types, the result would be strange. How to compare the equality of "abc " and" abc " can return false? It cannot, so the comparison is made on top of the identity, which in the case of type string is its value and not its reference.

See working on ideone. And no .NET Fiddle. Also I put on GitHub for future reference .

 26
Author: Maniero, 2020-09-04 10:55:51

Basically works like this:

  • (If the type is primitive (that is, Boolean (bool), Byte (byte), SByte (sbyte), Int16 (short), UInt16, Int32 (int), UInt32 (uint), Int64 (long), UInt64 (ulong), IntPtr, UIntPtr, Char (char), Double (double), or Single (single)), a comparison of the two variables will be the value;
  • any other object, the comparison will be property to property, but in this case it is quite easy an object not to be equal to others.
    • There is also the possibility to test if the references of the variables are equal, using the method System.Object.ReferenceEquals();
    • if the == operator is used for comparison between two objects, the method actually used is ReferenceEquals.
      • the exception for this case is structs. where it is mandatory to overload the == operator to be able to use it.

In this case, What C # offers is the possibility to rewrite the method Equals for a specific class, preferably to make comparison rules more flexible.

 15
Author: Leonel Sanches da Silva, 2014-06-02 16:02:53

If by "primitive" types you mean those for which the Framework has synonyms, such as System.Int32 (int), System.Boolean (bool), System.DateTime (DateTime) etc.

If they are structs, you must necessarily overload the == operator to be able to use it. The Equals method, for at least two instances of the same type, will compare the values of struct members, and return true if there is a member-to-Member match. Whether both operators will give the same result or not depends on the your implementation of == overhead.

For types by reference, the operator ==, not being overloaded, will compare the references, not the values themselves. Thus, two equivalent objects (i.e. an instance of any type and a deep clone) can be "different" when compared to ==. The Equals method, which can also be overloaded, does member-to-member comparison for several native types - but this is a matter of implementing each type, and not a crude comparison like that of structs.

 5
Author: Garoto de Programa, 2014-06-02 15:59:28

If you have a Null Object, Equals will not work. For example:

    String nome = null
    if (nome.equals("Nome"){
       //Faça alguma coisa
    }

This code will return a NullPointerException error because if name is null it is not possible to access the equals method. For primitive types, it is recommended to use the ==.

EDIT: sorry, I answered as if in Java. I believe that maybe it applies to C #

 -1
Author: LeoSantana, 2014-06-02 15:52:25