¿Cómo puedo comprobar si hay valores nulos en una sobrecarga del operador '==' sin recursividad infinita?
-
09-06-2019 - |
Pregunta
Lo siguiente provocará una recursividad infinita en el método de sobrecarga del operador ==
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (foo1 == null) return foo2 == null;
return foo1.Equals(foo2);
}
¿Cómo verifico si hay nulos?
Solución
Usar ReferenceEquals
:
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (object.ReferenceEquals(null, foo1))
return object.ReferenceEquals(null, foo2);
return foo1.Equals(foo2);
}
Otros consejos
Convertir a objeto en el método de sobrecarga:
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
Usar ReferenciaEs igual
.Desde el Foros de MSDN:
public static bool operator ==(Foo foo1, Foo foo2) {
if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
if (ReferenceEquals(foo2, null)) return false;
return foo1.field1 == foo2.field2;
}
Intentar Object.ReferenceEquals(foo1, null)
De todos modos, no recomendaría sobrecargar el ==
operador;debe usarse para comparar referencias y usar Equals
para comparaciones "semánticas".
Si he anulado bool Equals(object obj)
y quiero el operador ==
y Foo.Equals(object obj)
para devolver la misma respuesta, normalmente implemento el !=
operador así:
public static bool operator ==(Foo foo1, Foo foo2) {
return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
return !object.Equals(foo1, foo2);
}
El operador ==
Entonces, después de hacer todas las comprobaciones nulas, terminaré llamando. foo1.Equals(foo2)
que he anulado para hacer la verificación real si los dos son iguales.
Si está utilizando C# 7 o posterior, puede utilizar la coincidencia de patrones constante nula:
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
Esto le proporciona un código un poco más claro que el que llama al objeto.ReferenceEquals(foo1, null)
Mi enfoque es hacer
(object)item == null
en el que estoy confiando object
El propio operador de igualdad que no puede salir mal.O un método de extensión personalizado (y una sobrecarga):
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null;
}
public static bool IsNull<T>(this T? obj) where T : struct
{
return !obj.HasValue;
}
o para manejar más casos, puede ser:
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
La restricción impide IsNull
sobre tipos de valores.Ahora es tan dulce como llamar
object obj = new object();
Guid? guid = null;
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error
lo que significa que tengo un estilo coherente/no propenso a errores para comprobar si hay nulos en todo momento.yo también he encontrado (object)item == null
es muy, muy, ligeramente más rápido que Object.ReferenceEquals(item, null)
, pero solo si es importante (¡actualmente estoy trabajando en algo en lo que tengo que microoptimizar todo!).
Para ver una guía completa sobre la implementación de controles de igualdad, consulte ¿Cuál es la "mejor práctica" para comparar dos instancias de un tipo de referencia?
La estática Equals(Object, Object)
método indica si dos objetos, objA
y objB
, son iguales.También le permite probar objetos cuyo valor es null
por la igualdad.se compara objA
y objB
por la igualdad de la siguiente manera:
- Determina si los dos objetos representan la misma referencia de objeto.Si lo hacen, el método devuelve
true
.Esta prueba equivale a llamar alReferenceEquals
método.Además, si ambosobjA
yobjB
sonnull
, el método devuelvetrue
. - Determina si
objA
oobjB
esnull
.Si es así, regresafalse
.Si los dos objetos no representan la misma referencia de objeto y tampoco lo esnull
, llamaobjA.Equals(objB)
y devuelve el resultado.Esto significa que siobjA
anula elObject.Equals(Object)
método, se llama a esta anulación.
.
public static bool operator ==(Foo objA, Foo objB) {
return Object.Equals(objA, objB);
}
respondiendo más a operador primordial cómo comparar con nulo eso redirige aquí como un duplicado.
En los casos en los que esto se hace para admitir objetos de valor, la nueva notación me resulta útil y me gusta asegurarme de que solo haya un lugar donde se realice la comparación.Además, aprovechar Object.Equals(A, B) simplifica las comprobaciones nulas.
Esto sobrecargará ==, !=, Equals y GetHashCode
public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
public override bool Equals(object other) => Equals(other as ValueObject );
public bool Equals(ValueObject other) {
return !(other is null) &&
// Value comparisons
_value == other._value;
}
public override int GetHashCode() => _value.GetHashCode();
Para objetos más complicados, agregue comparaciones adicionales en Equals y un GetHashCode más completo.
En realidad, existe una forma más sencilla de comparar null
en este caso:
if (foo is null)
¡Eso es todo!
Esta característica se introdujo en C# 7.
Un error común en las sobrecargas del operador == es usar
(a == b)
,(a ==null)
, o(b == null)
para comprobar la igualdad de referencia.esto en cambio resultados en una llamada al operador sobrecargado ==, provocando uninfinite loop
.UsarReferenceEquals
o arrojar el tipo a objeto, para evitar el bucle.
mira esto
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
return false;
}
referencia Directrices para sobrecargar Equals() y Operador ==
Puede intentar utilizar una propiedad de objeto y detectar la NullReferenceException resultante.Si la propiedad que prueba se hereda o se anula de Object, entonces esto funciona para cualquier clase.
public static bool operator ==(Foo foo1, Foo foo2)
{
// check if the left parameter is null
bool LeftNull = false;
try { Type temp = a_left.GetType(); }
catch { LeftNull = true; }
// check if the right parameter is null
bool RightNull = false;
try { Type temp = a_right.GetType(); }
catch { RightNull = true; }
// null checking results
if (LeftNull && RightNull) return true;
else if (LeftNull || RightNull) return false;
else return foo1.field1 == foo2.field2;
}