Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Как правильно: Object.ReferenceEquals или Оператор ==?

#c_sharp #net


Как правильно сравнить два объекта, чтобы определить являются ли экземпляры  объектов
одним и тем же экземпляром или нет?
Что правильно/лучше использовать: метод Object.ReferenceEquals 

if(Object.ReferenceEquals(a, b)) { /*...*/ }


или Оператор == 

if(a == b) { /*...*/ }      // object a, b;




ВНИМАНИЕ: не путайте Equals c ReferenceEquals
    


Ответы

Ответ 1



Мне кажется, здесь более важна не техническая сторона вопроса, а читабельность кода. Если у вас есть два объекта a и b, и вы сравниваете их через ==, может быть вызван не operator == (object, object), а перегруженный оператор сравнения. Это значит, что семантика == шире, чем простое сравнение ссылок. Если ваша цель — распознать именно один и тот же экземпляр, я бы посоветовал использовать именно ReferenceEquals, т. к. при этом вы прямо «говорите» то, что хотите. Преимущество в компактности кода у ==, на мой взгляд, даёт недостаточный выигрыш по сравнению в преимуществе в понятности кода. Да и для правильности сравнения нужно приведение типа к object, которое тоже не добавляет краткости. С другой стороны, если вам нужно установить не равенство ссылок, а логическую тождественность объектов, для этого более правильным может оказаться использование (возможно перегруженного) оператора ==. Поэтому используйте то, что вам нужно. Универсального совета нет, каждый из методов имеет своё применение. Ещё одна техническая деталь — в обобщённых (generic-) методах на текущий момент (C# 6) невозможно сообщить компилятору, что у типа есть оператор ==. Поэтому сравнение через == в generic-методе может дать не такой же результат, как в обычном методе.

Ответ 2



В исходнике .NET Framework, метод Object.ReferenceEquals определен следующим образом: public static bool ReferenceEquals (Object objA, Object objB) { return objA == objB; } В методе вызывается Оператор ==, значит если сравнивать Object'ы, то нет разницы. Реализация Equals следующая public static bool Equals(Object objA, Object objB) { if (objA==objB) { return true; } if (objA==null || objB==null) { return false; } return objA.Equals(objB); } ВНИМАНИЕ: если Оператор == вернет false, то может быть вызван метод public virtual bool Equals(Object obj) -- метод виртуальный, может быть переопределен в производных классах и полагаться на него не надо. Для идентификации можно использовать метод RuntimeHelpers.GetHashCode using System.Runtime.CompilerServices; if(RuntimeHelpers.GetHashCode(a) == RuntimeHelpers.GetHashCode(b)) { /*... */ } ВНИМАНИЕ: если a и b это строки, то RuntimeHelpers.GetHashCode для одинаковых строк может вернуть разные хэш-коды. Например, var s1 = "321"; var s2 = String.Concat(new List { "3", "2", "1" }); var r1 = s1 == s2; // true var r2 = RuntimeHelpers.GetHashCode(s1) == RuntimeHelpers.GetHashCode(s2); // false Дело в том, что литералы (строки, определенные в коде, такие как "321"), добавляются во внутренний пул строк в целях экономии памяти. А строки, собранные из частей, в пул не добавляются. Поэтому получается, что s1 и s2 - разные объекты. Но s2 можно интернировать (т.е. добавить в пул строк) s2 = String.Intern(s2); Т.к. в пуле строк уже есть s1, то s2 и s1 будут указывать на один и тот же строковой объект, поэтому хэш-коды у s1 и s2 станут равны. var r3 = RuntimeHelpers.GetHashCode(s1) == RuntimeHelpers.GetHashCode(s2); // true

Комментариев нет:

Отправить комментарий