What is the difference in the use of the Equals method for the ==operator?

Asked

Viewed 5,615 times

25

What is the difference in the use of the method Equals for the operator == in situations of comparison between (1) value types and (2) reference types?

  • 3

    Just a hint, in C# there is no concept of primitive types. There are types by value (struct and enum) and types by reference (class, interface, delegate, etc.). See more on http://answall.com/questions/14490/alocacao-de-memoria-em-c-tipos-valor-e-typos-referenced/14492#14492

4 answers

26


According to Microsoft in text written by Jon Skeet:

The method Equals is only a virtual method defined in System.Object, and may be superimposed by any classes that choose to do so. The == is an operator that can be overloaded by classes, when she usually 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 implementation of Equals does in System.Object.

Value types do not provide an overload for == by default. However, most types of values provided by . NET provide their own overhead. A standard implementation of Equals() for a type of value 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 pairs of references within the two values being compared.

However, the main difference between the two types of comparison under normal conditions of use (where it is unlikely that you define your own types of value) is polymorphism. Operators are overloaded, not overlapped (override), which means that unless the compiler knows to call the more specific version, it will only call the identity version. 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 unburdened version of == since he does not know the content 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 reference types, the use is equal to when you want to test equality instead of reference identity. The exception is to strings - compare strings with == makes things much simpler and more readable, but it is necessary to remember that both sides of the operator should be expressions of the type string, to get the comparison to work properly.

For value types, it is usually used == for better readability. It gets more complicated if a type of value, provides an overload to == which acts differently to Equals, but this would be such a badly designed situation.


Summing it up:

You choose what each should do according to the intuitive expectation of the programmer.

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

Already the method Equals() expects the comparison to be based on the value of the type. That is, based on their value, 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 == choose to use the Equals() or the implementation of ReferenceEquals().

The operator == is static and works more like an extension method.

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

  • the static method of object (Equals(object, object));
  • the virtual method 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 well as semantics is more important than the linearity of the functioning. See the examples:

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

This occurs because of a flame technique interning trying to repurpose an existing allocation of a string identical to the one you try to allocate again. 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 == to adopt the comparison of references as usual with types by reference the result would be strange. How the equality comparison of "abc" and "abc" can return false? Can not, so the comparison is made on the identity, that in the case of type string is its value and not its reference.

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

15

Basically it works like this:

  • Whether the type is primitive (i.e., 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)), the comparison between two variables shall be by value;
  • Any other object, the comparison will be property by property, but in this case, it is quite easy for one object not to be equal to another.
    • There is also the possibility of testing whether 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 will be the ReferenceEquals.
      • The exception for this case are structs. where it is mandatory to burden the operator == in order to use it.

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

  • 1

    Se for usado o operador == para comparação entre dois objetos, o método utilizado na verdade será o ReferenceEquals When type is by reference. For structs it is mandatory to overload the == to be able to use it. BTW +1

  • True. I will supplement the reply. Thank you!

5

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 need to overload the operator == to be able to use it. Already the method Equals, For at least two instances of the same type, it will compare the values of the members of the struct, and return true if there is a member-by-member match. Whether both operators will give the same result or not depends on their implementation of the ==.

Already for reference types, the operator ==, not being overloaded, will compare the references, not the values themselves. Thus, two equivalent objects (i.e.: an instance of some kind and a deep clone) can be "different" when compared to the ==. Already the method Equals, which can also be overburdened, makes member-to-member comparison for various native types - but this is a matter of implementation of each type, not a gross comparison like that of structs.

-1

If Voce has 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 it were in Java. I believe that maybe it applies to C#

  • 1

    Caso voce tenha um objeto do tipo nulo Null is not a type. And the code will give error because you are calling a method from a null reference. The same goes for Java, only changes the exception name. If nome had a value, you could pass null to the Equals method (which is with capital 'E').

Browser other questions tagged

You are not signed in. Login or sign up in order to post.