Как проверить наличие нулей в перегрузке оператора '==' без бесконечной рекурсии?
Следующее вызовет бесконечную рекурсию для метода перегрузки оператора ==
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);
}
Как проверить наличие нулей?
12 ответов
Использование 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);
}
Приведение к объекту в методе перегрузки:
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
Если вы используете C# 7 или более позднюю версию, вы можете использовать сопоставление с образцом нулевой константы:
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
Это дает вам немного более аккуратный код, чем вызывающий объект.ReferenceEquals(foo1, null)
Использование ReferenceEquals
, С форумов 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;
}
Если бы я переопределил bool Equals(object obj)
и я хочу оператора ==
а также Foo.Equals(object obj)
чтобы вернуть тот же ответ, я обычно реализую !=
Оператор, как это:
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);
}
Оператор ==
затем после выполнения всех нулевых проверок для меня в конечном итоге вызов foo1.Equals(foo2)
что я переопределил, чтобы сделать фактическую проверку, если два равны.
На самом деле существует более простой способ проверки null
в этом случае:
if (foo is null)
Это оно!
Эта функция была введена в C# 7
Пытаться Object.ReferenceEquals(foo1, null)
Во всяком случае, я бы не рекомендовал перегружать ==
оператор; это должно быть использовано для сравнения ссылок и использования Equals
для "семантических" сравнений.
Мой подход заключается в том, чтобы сделать
(object)item == null
на который я полагаюсь object
собственный оператор равенства, который не может пойти не так. Или пользовательский метод расширения (и перегрузка):
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;
}
или для обработки большего количества случаев, может быть:
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
Ограничение препятствует IsNull
по типам значений. Теперь это так же сладко, как звонить
object obj = new object();
Guid? guid = null;
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error
это означает, что у меня есть один непротиворечивый / не подверженный ошибкам стиль проверки на наличие нулей во всем. Я также нашел (object)item == null
очень очень очень немного быстрее, чемObject.ReferenceEquals(item, null)
, но только если это имеет значение (в настоящее время я работаю над чем-то, что мне нужно для микрооптимизации всего!).
Чтобы ознакомиться с полным руководством по реализации проверок на равенство, см. Раздел "Наилучшая практика" для сравнения двух экземпляров ссылочного типа?
Для современного сжатого синтаксиса:
public static bool operator ==(Foo x, Foo y)
{
return x is null ? y is null : x.Equals(y);
}
public static bool operator !=(Foo x, Foo y)
{
return x is null ? !(y is null) : !x.Equals(y);
}
Отвечая больше переопределяющему оператору, как сравнивать со значением NULL, которое перенаправляет сюда как дубликат.
В тех случаях, когда это делается для поддержки объектов-значений, я нахожу новую нотацию удобной и хочу убедиться, что сравнение выполняется только в одном месте. Также использование Object.Equals(A, B) упрощает нулевые проверки.
Это перегрузит ==,!=, Equals и 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();
Для более сложных объектов добавьте дополнительные сравнения в Equals и более богатый GetHashCode.
Статический Equals(Object, Object)
метод указывает, есть ли два объекта, objA
а также objB
, равны. Это также позволяет вам тестировать объекты, чье значение null
для равенства. Это сравнивает objA
а также objB
для равенства следующим образом:
- Он определяет, представляют ли два объекта одну и ту же ссылку на объект. Если они это делают, метод возвращает
true
, Этот тест эквивалентен вызовуReferenceEquals
метод. Кроме того, если обаobjA
а такжеobjB
являютсяnull
метод возвращаетtrue
, - Это определяет, либо
objA
или жеobjB
являетсяnull
, Если так, то возвращаетсяfalse
, Если два объекта не представляют одну и ту же ссылку на объект, и ни один из них не являетсяnull
это вызываетobjA.Equals(objB)
и возвращает результат. Это означает, что еслиobjA
переопределяетObject.Equals(Object)
метод, это переопределение вызывается.
,
public static bool operator ==(Foo objA, Foo objB) {
return Object.Equals(objA, objB);
}
Распространенной ошибкой в перегрузках оператора == является использование
(a == b)
,(a ==null)
, или же(b == null)
проверить на референтное равенство. Вместо этого это приводит к вызову перегруженного оператора ==, вызываяinfinite loop
, использованиеReferenceEquals
или приведите тип к объекту, чтобы избежать цикла.
проверить это
// 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;
}
Вы можете попытаться использовать свойство объекта и перехватить результирующее исключение NullReferenceException. Если свойство, которое вы пробуете, унаследовано или переопределено от Object, то это работает для любого класса.
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;
}