Как проверить наличие ошибок в перегрузке оператора '==' без бесконечной рекурсии?

Следующее приведет к бесконечной рекурсии по методу перегрузки оператора ==

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