Buscar..


Observaciones

Cada implementación de Equals debe cumplir los siguientes requisitos:

  • Reflexivo : un objeto debe igualarse a si mismo.
    x.Equals(x) devuelve true .

  • Simétrico : no hay diferencia si comparo x con y o con y con x - el resultado es el mismo.
    x.Equals(y) devuelve el mismo valor que y.Equals(x) .

  • Transitivo : si un objeto es igual a otro objeto y este es igual a un tercero, el primero debe ser igual al tercero.
    si (x.Equals(y) && y.Equals(z)) devuelve true , entonces x.Equals(z) devuelve true .

  • Consistente : si compara un objeto con otro varias veces, el resultado es siempre el mismo.
    Las invocaciones sucesivas de x.Equals(y) devuelven el mismo valor siempre que los objetos a los que se hace referencia con x e y no se modifiquen.

  • Comparación a nulo : Ningún objeto es igual a null .
    x.Equals(null) devuelve false .

Implementaciones de GetHashCode :

  • Compatible con Equals : si dos objetos son iguales (lo que significa que Equals devuelve true), GetHashCode debe devolver el mismo valor para cada uno de ellos.

  • Rango grande : si dos objetos no son iguales ( Equals falso), debería haber una alta probabilidad de que sus códigos hash sean distintos. El hashing perfecto a menudo no es posible ya que hay un número limitado de valores para elegir.

  • Barato : debería ser barato calcular el código hash en todos los casos.

Consulte: Pautas para la sobrecarga de Equals () y Operator ==

Por defecto es igual al comportamiento.

Equals se declara en la propia clase de Object .

public virtual bool Equals(Object obj);

Por defecto, Equals tiene el siguiente comportamiento:

  • Si la instancia es un tipo de referencia, entonces Equals devolverá verdadero solo si las referencias son las mismas.

  • Si la instancia es un tipo de valor, entonces Equals devolverá verdadero solo si el tipo y el valor son los mismos.

  • string es un caso especial. Se comporta como un tipo de valor.

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //areFooClassEqual: False
            Foo fooClass1 = new Foo("42");
            Foo fooClass2 = new Foo("42");
            bool areFooClassEqual = fooClass1.Equals(fooClass2);
            Console.WriteLine("fooClass1 and fooClass2 are equal: {0}", areFooClassEqual);
            //False

            //areFooIntEqual: True
            int fooInt1 = 42;
            int fooInt2 = 42;
            bool areFooIntEqual = fooInt1.Equals(fooInt2);
            Console.WriteLine("fooInt1 and fooInt2 are equal: {0}", areFooIntEqual);

            //areFooStringEqual: True
            string fooString1 = "42";
            string fooString2 = "42";
            bool areFooStringEqual = fooString1.Equals(fooString2);
            Console.WriteLine("fooString1 and fooString2 are equal: {0}", areFooStringEqual);
        }
    }

    public class Foo
    {
        public string Bar { get; }

        public Foo(string bar)
        {
            Bar = bar;
        }
    }
}

Escribiendo un buen reemplazo de GetHashCode

GetHashCode tiene efectos de rendimiento importantes en Dictionary <> y HashTable.

Buenos métodos GetHashCode

  • debe tener una distribución uniforme
    • cada entero debe tener una probabilidad aproximadamente igual de regresar para una instancia aleatoria
    • Si su método devuelve el mismo entero (por ejemplo, la constante '999') para cada instancia, tendrá un mal rendimiento.
  • debería ser rápido
    • Estos NO son hashes criptográficos, donde la lentitud es una característica
    • Cuanto más lenta sea la función hash, más lento será su diccionario.
  • debe devolver el mismo HashCode en dos instancias que Equals evalúa como verdadero
    • si no lo hacen (por ejemplo, porque GetHashCode devuelve un número aleatorio), es posible que los elementos no se encuentren en una List , Dictionary o similar.

Un buen método para implementar GetHashCode es usar un número primo como valor de inicio y agregar los hashcodes de los campos del tipo multiplicado por otros números primos a eso:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 3049; // Start value (prime number).

        // Suitable nullity checks etc, of course :)
        hash = hash * 5039 + field1.GetHashCode();
        hash = hash * 883 + field2.GetHashCode();
        hash = hash * 9719 + field3.GetHashCode();
        return hash;
    }
}

Solo los campos que se usan en el método Equals deben usarse para la función hash.

Si necesita tratar el mismo tipo de diferentes maneras para Dictionary / HashTables, puede usar IEqualityComparer.

Sobrescribe Equals y GetHashCode en tipos personalizados

Para una Person clase como:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); //false because it's reference Equals

Pero definiendo Equals y GetHashCode como sigue:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }

    public override bool Equals(object obj)
    {
        var person = obj as Person;
        if(person == null) return false;
        return Name == person.Name && Age == person.Age; //the clothes are not important when comparing two persons
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode()*Age;
    }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); // result is true

También al usar LINQ para hacer diferentes consultas sobre personas, se verificará Equals y GetHashCode :

var persons = new List<Person>
{
     new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
     new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
     new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();//distinctPersons has Count = 2

Equals y GetHashCode en IEqualityComparator

Para el tipo de Person dado:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

List<Person> persons = new List<Person>
{
    new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
    new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
    new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();// distinctPersons has Count = 3

Pero definiendo Equals y GetHashCode en un IEqualityComparator :

public class PersonComparator : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name && x.Age == y.Age; //the clothes are not important when comparing two persons;
    }

    public int GetHashCode(Person obj) { return obj.Name.GetHashCode() * obj.Age; }
}

var distinctPersons = persons.Distinct(new PersonComparator()).ToList();// distinctPersons has Count = 2

Tenga en cuenta que para esta consulta, dos objetos se han considerado iguales si tanto el valor Equals devuelto como el código GetHashCode han devuelto el mismo código hash para las dos personas.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow