El ejercicio ilustra varias propiedades de la igualdad en C#:
Object.Equals()== y !=. Esto se considera más idiomático que usar el método Equals(), que también está disponible con estos tipos. Los desarrolladores de Java deberían estar atentos, cuando trabajan con strings, al hecho de que == compara por valor en C#, pero por referencia en Java, cuando vuelven a su antiguo lenguaje.Equals() heredado de object. Si tu objetivo con la prueba de igualdad es asegurarte de que dos objetos son exactamente la misma instancia, basta con confiar en la implementación de object. Si no, tendrás que sobrescribir object.Equals().Equals().Equals() sobrescrito contendrá comparaciones de igualdad de miembros de tipos simples mediante == y de tipos de referencia mediante llamadas recursivas a Equals().class StatusBar
{
private readonly int width = 200, height = 20;
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return width == (other as StatusBar).width && height == (other as StatusBar).height;
}
}
class Window
{
private readonly string title = "Main";
private readonly StatusBar statusBar = new StatusBar();
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
}
}
object.ReferenceEquals() se usa para comparar dos objetos y detectar si son una y la misma instancia. Esto aporta claridad y es necesario cuando Equals() y == se han sobrescrito o sobrecargado.var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true
public override bool Equals(object obj), los IDE suelen generar la sobrecarga protected bool Equals(FacialFeatures other) para usarla cuando hay herencia. Una clase derivada puede llamar al Equals() de la clase base y luego añadir su propia comprobación.== a menos que hayas sobrecargado el operador ==, además del método Equals() de tu clase (consulta el ejercicio operator-overloading), o a menos que solo te importe que las referencias sean iguales.structs se tratan en el ejercicio structs.tuples se trata en el ejercicio tuples.protected bool Equals(T other)
{
return field1 == other.field1 && field2.Equals(other.field2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((T) obj);
}
SequenceEquals(), pero si no hay LINQ, habrá que recorrer ambas colecciones y comparar los elementos uno a uno.== y != con tus propias clases, consulta el ejercicio operator-overloading.Object.GetHashCode()object.GetHashCode() devuelve un código hash en forma de entero de 32 bits. Las clases de diccionario y de conjunto, como Dictionary<T> y HashSet<T>, usan el código hash para almacenar y recuperar objetos de forma eficiente. En el caso de los diccionarios, el hashing está relacionado con las claves.Equals(), también sobrescribas GetHashCode(). Hay una relación entre Equals() y GetHashCode() que debe cumplirse para el correcto funcionamiento de las clases de diccionario y de conjunto hash, y de cualquier otra que use un código hash. Se espera que implementes el método de forma que no se tiendan trampas a los responsables del mantenimiento que puedan añadir más adelante una colección basada en códigos hash.Equal() devuelve true), GetHashCode() debe devolver el mismo valor para ambos objetos. Esto no se aplica en la dirección contraria. No es simétrico. Imagina una función de búsqueda que primero va a un «bucket» basándose en el código hash y luego selecciona el elemento concreto mediante la prueba de igualdad.HashCode.Combine() y pasarle los valores usados en la prueba de igualdad (o un subconjunto). Ten en cuenta que, cuanta más información le proporciones a Combine(), más eficiente será probablemente la implementación del hash.public class Assessment
{
private int rating;
private Person boss;
public override int GetHashCode()
{
return HashCode.Combine(rating, boss);
}
}
Para mejorar ligeramente el rendimiento, sobre todo cuando los objetos pertenecen a colecciones, puedes añadir un miembro sobrecargado public bool Equals(T other).
Esto ahorrará cierta cantidad de comprobaciones de tipo para los tipos de referencia y ahorrará un paso de boxing para los tipos de valor, ya que no será necesario convertirlos a un objeto (boxing) como argumento de public override bool Equals(object other).
Si añades la interfaz IEquatable<T> a tu clase, tendrás que implementar la sobrecarga. A menos que tu código contenga rutinas que reciban objetos de tipo IEquatable<T> (y que presuntamente hagan algo interesante relacionado con la igualdad, independientemente de la clase que lo implemente), en realidad no hay ninguna otra razón de peso para incluir la interfaz.
IEqualityComparer<T>Si tienes una clase que puede identificarse de forma única de dos maneras distintas, por ejemplo una clase Person que tiene un SSID y una dirección de correo electrónico única, .NET ofrece un mecanismo para que dos colecciones distintas usen pruebas de igualdad y códigos hash diferentes. Cada una puede tener una implementación diferente de IEqualityComparer<T> con su propio método Equals() y su propio GetHashCode(). Puedes tener un diccionario con clave de SSID y otro con clave de dirección de correo electrónico.
Cuando IEqualityComparer<T> está en juego, lo habitual sigue siendo sobrescribir Equals() y GetHashCode() en la clase de tu elemento para evitar problemas fuera de las clases de colección.
Una consideración al usar IEqualityComparer<T> es que los métodos privados, etc., no estarán disponibles para la prueba de igualdad.
Si solo hay una colección con hash en juego, puede ser mejor evitar IEqualityComparer<T> y encapsular la igualdad y el código hash en el propio objeto. No es lo ideal, para empezar, que el compilador no pueda hacer cumplir una dependencia clave como la que existe entre el objeto y la colección con hash, pero si la lógica de hash y de comparación está en otro sitio, aún sería más fácil que un responsable del mantenimiento modificara los miembros de una clase sin tener en cuenta las consecuencias para la colección.
Un tipo primitivo que puede sorprender a quien no tenga cuidado es comprobar la igualdad de valores de coma flotante. Esto se trata en el documento about.md del concepto floating-point-numbers.
Este artículo muestra algunas de las decisiones que hay que tomar en relación con la igualdad y la herencia. Solo te concernirán si estás manipulando instancias de una clase y de una clase derivada y necesitas comprobar la igualdad entre ambas.