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

StackOverflow https://stackoverflow.com/questions/73713

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Как мне проверить наличие нулей?

Это было полезно?

Решение

Использование 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);
}

Использование Ссылочные значения.Из самого Форумы 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;
}

Попробуй Object.ReferenceEquals(foo1, null)

В любом случае, я бы не рекомендовал перегружать ==оператор;его следует использовать для сравнения ссылок и использовать Equals для "семантических" сравнений.

Если я переопределил 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) который я переопределил, чтобы выполнить фактическую проверку, равны ли они.

Если вы используете 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)

Мой подход заключается в том, чтобы сделать

(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), но только если это имеет значение (в настоящее время я работаю над чем-то, где мне нужно все микрооптимизировать!).

Чтобы ознакомиться с полным руководством по внедрению проверок на равенство, см. Что такое "Наилучшая практика" Для сравнения двух экземпляров ссылочного типа?

Статический 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);
}

отвечая больше на переопределяющий оператор как сравнить с null это перенаправляет сюда как дубликат.

В тех случаях, когда это делается для поддержки объектов Value, я нахожу новую нотацию удобной и хотел бы убедиться, что есть только одно место, где производится сравнение.Также использование Object.Equals(A, B) упрощает проверку null.

Это приведет к перегрузке ==, !=, 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.

На самом деле существует более простой способ проверки на null в данном случае:

if (foo is null)

Вот и все!

Эта функция была введена в C # 7

Распространенной ошибкой при перегрузках operator == является использование (a == b), (a ==null), или (b == null) чтобы проверить равенство ссылок.Это вместо приводит к вызов перегруженного оператора ==, вызывающий infinite loop.Использование ReferenceEquals или приведите тип к Object, чтобы избежать цикла .

посмотри на это

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

ссылка Рекомендации по перегрузке Equals() и Operator ==

Вы можете попытаться использовать свойство объекта и перехватить результирующее исключение 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top