равенство и тождество объектов
Часто разработчикам приходится создавать код сравнения объектов. В частности, это
необходимо, когда объекты размещаются в коллекциях и требуется писать код для
сортировки, поиска и сравнения отдельных элементов в коллекции. В этом разделе
рассказывается о равенстве и тождестве объектов, а также о том, как определять
тип, который правильно реализует равенство объектов.
У типа
System.Object
есть виртуальный метод
Equals
, который возвращает
true
для двух «равных» объектов. Вот как выглядит реализация метода
Equals
для
Object
:
public class Object {
public virtual Boolean Equals(Object obj) {
// Если обе ссылки указывают на один и тот же объект,
// значит, эти объекты равны
if (this == obj) return true;
// Предполагаем, что объекты не равны
return false;
}
}
173
Упаковка.и.распаковка.значимых.типов
На первый взгляд эта реализация выглядит вполне разумно: сравниваются две
ссылки, переданные в аргументах
this
и
obj
, и если они указывают на один объект,
возвращается
true
, в противном случае возвращается
false
. Это кажется логичным,
так как
Equals
«понимает», что объект равен самому себе. Однако если аргументы
ссылаются на разные объекты,
Equals
сложнее определить, содержат ли объекты
одинаковые значения, поэтому возвращается
false
. Иначе говоря, оказывается,
что стандартная реализация метода
Equals
типа
Object
реализует проверку на
тождество, а не на равенство значений.
Как видите, приведенная здесь стандартная реализация никуда не годится.
Проблема немедленно становится очевидной, стоит вам подумать об иерархиях
наследования классов и правильном переопределении
Equals
. Вот как должна
действовать правильная реализация метода
Equals
:
1. Если аргумент
obj
равен
null
, вернуть
false
, так как ясно, что текущий объект,
указанный в
this
, не равен
null
при вызове нестатического метода
Equals
.
2. Если аргументы
obj
и
this
ссылаются на объекты одного типа, вернуть
true
.
Этот шаг поможет повысить производительность в случае сравнения объектов
с многочисленными полями.
3. Если аргументы
obj
и
this
ссылаются на объекты разного типа, вернуть
false
.
Понятно, что результат сравнения объектов
String
и
FileStream
равен
false
.
4. Сравнить все определенные в типе экземплярные поля объектов
obj
и
this
.
Если хотя бы одна пара полей не равна, вернуть
false
.
5. Вызвать метод
Equals
базового класса, чтобы сравнить определенные в нем
поля. Если метод
Equals
базового класса вернул
false
, тоже вернуть
false
,
в противном случае вернуть
true
.
Учитывая это, компания Microsoft должна была бы реализовать метод
Equals
типа
Object
примерно так:
public class Object {
public virtual Boolean Equals(Object obj) {
// Сравниваемый объект не может быть равным null
if (obj == null) return false;
// Объекты разных типов не могут быть равны
if (this.GetType() != obj.GetType()) return false;
// Если типы объектов совпадают, возвращаем true при условии,
// что все их поля попарно равны.
// Так как в System.Object не определены поля,
// следует считать, что поля равны
return true;
}
}
Однако, поскольку в Microsoft метод
Equals
реализован иначе, правила соб-
ственной реализации
Equals
намного сложнее, чем кажется. Если ваш тип пере-
174
Do'stlaringiz bilan baham: |