Ответ 1
Нет простого ответа на этот вопрос. Любой, кто говорит, всегда использует тот или иной, дает мне советы, по моему мнению.
На самом деле существует несколько разных методов, которые вы можете вызывать для сравнения экземпляров объектов. Учитывая два экземпляра объекта a
и b
, вы можете написать:
-
Object.Equals(a,b)
-
Object.ReferenceEquals(a,b)
-
a.Equals(b)
-
a == b
Все могут сделать разные вещи!
Object.Equals(a,b)
будет (по умолчанию) выполнять сравнение ссылочного равенства по ссылочным типам и поразрядное сравнение типов значений. Из документации MSDN:
Стандартная реализация Equals поддерживает ссылочное равенство для ссылочные типы и побитовое равенство для типов значений. Справочное равенство означает ссылки на объекты, которые сравнение относится к одному и тому же объекту. Поразрядное равенство означает, что объекты которые сравниваются, имеют один и тот же двоичный представление.
Обратите внимание, что производный тип может переопределить метод Equals реализовать равенство ценности. Стоимость равенство означает сравниваемые объекты имеют одинаковое значение, но разные двоичные представления.
Обратите внимание на последний параграф выше... мы поговорим об этом чуть позже.
Object.ReferenceEquals(a,b)
выполняет только сравнение сравнения ссылок. Если переданные типы являются типами значений в штучной упаковке, результат всегда false
.
a.Equals(b)
вызывает метод виртуального экземпляра Object
, который тип a
может переопределить, чтобы делать все, что он хочет. Вызов выполняется с использованием виртуальной диспетчеризации, поэтому выполняемый код зависит от типа времени выполнения a
.
a == b
вызывает статический перегруженный оператор типа ** времени компиляции * a
. Если реализация этого оператора вызывает методы экземпляра на a
или b
, это также может зависеть от типов параметров выполнения. Поскольку отправка основана на типах в выражении, следующие могут давать разные результаты:
Frog aFrog = new Frog();
Frog bFrog = new Frog();
Animal aAnimal = aFrog;
Animal bAnimal = bFrog;
// not necessarily equal...
bool areEqualFrogs = aFrog == bFrog;
bool areEqualAnimals = aAnimal = bAnimal;
Итак, есть уязвимость для проверки нулей с помощью operator ==
. На практике большинство типов не перегружают ==
- но никогда не гарантируется,
Метод экземпляра Equals()
здесь не лучше. Хотя реализация по умолчанию выполняет проверку ссылочных/побитовых равенств, тип может переопределить метод члена Equals()
, и в этом случае эта реализация будет вызвана. Реализация, предоставленная пользователем, может вернуть все, что захочет, даже при сравнении с нулевым.
Но как насчет статической версии Object.Equals()
, которую вы спрашиваете? Может ли это привести код пользователя? Ну, получается, что ответ ДА. Реализация Object.Equals(a,b)
расширяется до следующего:
((object)a == (object)b) || (a != null && b != null && a.Equals(b))
Вы можете попробовать это для себя:
class Foo {
public override bool Equals(object obj) { return true; } }
var a = new Foo();
var b = new Foo();
Console.WriteLine( Object.Equals(a,b) ); // outputs "True!"
Как следствие, для оператора можно: Object.Equals(a,b)
запустить код пользователя, когда ни один из типов в вызове null
. Обратите внимание, что Object.Equals(a,b)
не выполняет вызов версии экземпляра Equals()
, если любой из аргументов имеет значение null.
Короче говоря, вид поведения сравнения, который вы получаете, может значительно различаться в зависимости от того, какой метод вы выберете. Один комментарий здесь: Microsoft официально не документирует внутреннее поведение Object.Equals(a,b)
. Если вам нужна планка с железом, сравнивающая ссылку на null без какого-либо другого кода, вы хотите Object.ReferenceEquals()
:
Object.ReferenceEquals(item, null);
Этот метод делает намерение более четким - вы, в частности, ожидаете, что результатом будет сравнение двух ссылок для ссылочного равенства. Преимущество здесь в использовании чего-то вроде Object.Equals(a,null)
заключается в том, что менее вероятно, что кто-то придет позже и скажет:
"Эй, это неудобно, замените его на: a.Equals(null)
или a == null
которые потенциально могут быть разными.
Пусть, однако, введем некоторый прагматизм. До сих пор мы говорили о потенциале для разных модальностей сравнения, чтобы дать разные результаты. Хотя это, безусловно, так, есть определенные типы, где безопасно писать a == null
. Встроенные .NET-классы, такие как String
и Nullable<T>
, имеют хорошо определенную семантику для сравнения. Кроме того, они sealed
- предотвращают любое изменение их поведения через наследование. Следующее довольно часто (и правильно):
string s = ...
if( s == null ) { ... }
Не нужно (и уродливо) писать:
if( ReferenceEquals(s,null) ) { ... }
Поэтому в некоторых ограниченных случаях использование ==
является безопасным и соответствующим.