Как мне проверить наличие нулей при перегрузке оператора '==' без бесконечной рекурсии?
-
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;
}
Вы можете попытаться использовать свойство объекта и перехватить результирующее исключение 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;
}