¿Cómo verifico nulos en una sobrecarga de operador ‘==’ sin recursión infinita?

Lo siguiente causará recursión 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 los nulos?

Use 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); } 

Enviar 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); } 

Use ReferenceEquals . Desde los 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; } 

Pruebe Object.ReferenceEquals(foo1, null)

De todos modos, no recomendaría sobrecargar el operador == ; debe usarse para comparar referencias y usar Equals para las comparaciones “semánticas”.

Si he reemplazado bool Equals(object obj) y quiero que el operador == y Foo.Equals(object obj) devuelvan la misma respuesta, generalmente implemento el operador != esta manera:

 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 == luego de hacer todas las comprobaciones nulas para mí termina llamando a foo1.Equals(foo2) que he anulado para hacer la comprobación real si las dos son iguales.

Si está utilizando C # 7 o posterior, puede usar la coincidencia nula de patrones constantes:

 public static bool operator==(Foo foo1, Foo foo2) { if (foo1 is null) return foo2 is null; return foo1.Equals(foo2); } 

Esto le da un código ligeramente más ordenado que el objeto que llama. ReferenceEquals (foo1, null)

Mi enfoque es hacer

 (object)item == null 

sobre lo cual confío en el operador de igualdad del objeto que no puede salir mal. O un método de extensión personalizado (y una sobrecarga):

 public static bool IsNull(this T obj) where T : class { return (object)obj == null; } public static bool IsNull(this T? obj) where T : struct { return !obj.HasValue; } 

o para manejar más casos, puede ser:

 public static bool IsNull(this T obj) where T : class { return (object)obj == null || obj == DBNull.Value; } 

La restricción impide IsNull en los 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 constante / no propenso a error de comprobación de nulos en todo. También he encontrado que el (object)item == null es muy muy ligeramente más rápido que Object.ReferenceEquals(item, null) , pero solo si importa (¡Actualmente estoy trabajando en algo donde tengo que optimizar todo! )

Para ver una guía completa sobre la implementación de verificaciones de igualdad, consulte ¿Qué es “Mejores prácticas” para comparar dos instancias de un Tipo de referencia?

El método estático Equals(Object, Object) indica si dos objetos, objA y objB , son iguales. También le permite probar objetos cuyo valor sea null para la igualdad. Compara objA y objB para 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 es equivalente a llamar al método ReferenceEquals . Además, si tanto objA como objB son null , el método devuelve true .
  • Determina si objA u objB es null . Si es así, devuelve false . Si los dos objetos no representan la misma referencia de objeto y ninguno es null , llama a objA.Equals(objB) y devuelve el resultado. Esto significa que si objA anula el Object.Equals(Object) , se llama a esta anulación.

.

 public static bool operator ==(Foo objA, Foo objB) { return Object.Equals(objA, objB); } 

respondiendo más al operador principal cómo comparar a nulo que redirige aquí como un duplicado.

En los casos en que esto se hace para dar soporte a Value Objects, encuentro que la nueva notación es útil, y me gusta asegurar que solo haya un lugar donde se haga la comparación. También aprovechando Object.Equals (A, B) simplifica las verificaciones 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.

Un error común en las sobrecargas del operador == es usar (a == b) , (a ==null) o (b == null) para verificar la igualdad de referencia. Esto en cambio da como resultado una llamada al operador sobrecargado ==, causando un infinite loop . Use ReferenceEquals o coloque el tipo en Object para evitar el ciclo.

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; } 

Pautas de referencia para Sobrecarga Equals () y Operador ==

Puede intentar usar una propiedad de objeto y capturar la NullReferenceException resultante. Si la propiedad que intentas es heredada o anulada 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; }