Как проверить наличие ошибок в перегрузке оператора '==' без бесконечной рекурсии?
Следующее приведет к бесконечной рекурсии по методу перегрузки оператора ==
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);
}
Как проверить нули?
Ответы
Ответ 1
Используйте 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);
}
Ответ 2
Передача объекта в метод перегрузки:
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
Ответ 3
Используйте 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;
}
Ответ 4
Попробуйте Object.ReferenceEquals(foo1, null)
В любом случае, я бы не рекомендовал перегружать оператор ==
; его следует использовать для сравнения ссылок и использовать Equals
для "семантических" сравнений.
Ответ 5
Если я переопределил 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)
, который я переопределил, чтобы выполнить фактическую проверку, если они равны.
Ответ 6
Если вы используете С# 7 или более позднюю версию, вы можете использовать сопоставление с нулевым постоянным шаблоном:
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
Это дает вам немного более аккуратный код, чем тот, который вызывает объект. RueExquals (foo1, null)
Ответ 7
Мой подход - делать
(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)
, но только если это имеет значение (я сейчас работаю над чем-то, микро-оптимизировать все!).
Чтобы увидеть полное руководство по реализации проверок равенства, см. Что такое "Лучшая практика" Для сравнения двух экземпляров ссылочного типа?
Ответ 8
В этом случае на самом деле существует более простой способ проверки по null
:
if (foo is null)
Вот оно!
Эта функция была введена в С# 7
Ответ 9
Статический метод 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);
}
Ответ 10
отвечая больше на переопределяющий оператор, как сравнивать с нулевым, который перенаправляет сюда как дубликат.
В тех случаях, когда это делается для поддержки объектов-значений, я нахожу новую нотацию удобной и хочу убедиться, что сравнение выполняется только в одном месте. Также использование 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.
Ответ 11
Для современного сжатого синтаксиса:
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);
}
Ответ 12
Общей ошибкой в перегрузках оператора == является использование (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;
}
ссылка Рекомендации по перегрузке равных() и операторов ==
Ответ 13
Вы можете попытаться использовать свойство объекта и уловить полученное исключение 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;
}