Разработка приложений на языке C#. Полезные настройки. Особые спецификаторы формата, страница 19

Console.WriteLine("\n\nken.Equals(men[0]) = {0}", ken.Equals(men[0]));

Console.WriteLine("men[0].Equals(men[3]) = {0}", men[0].Equals(men[3]));

Основным принципом использования хеш-таблиц является то, что одинаковым объектам соответствуют одинаковые ключи. Запустите и проверьте, что происходит. Удовлетворяется ли этот принцип в нашем случае? Убедитесь, что (до перемещения) ссылкам на одни и те же объекты поставлены в соответствие одни и те же ключи, но после перемещения в памяти одного объекта соответствие нарушается. Теперь вы должны понять смысл предупреждений компилятора. Как Equals, так и operator==() (если вы его переопределили) считают, что объекты men[0] и men[3] равны, однако функция GetHashCode дает им разные ключи. Этот диссонанс скажется (приведет к алгоритмическим ошибкам), если мы будем пользоваться коллекцией типа Hashtable. Устраните этот недостаток, переопределив метод GetHashCode в классе Man. Используйте механизм Intellisense. В тело заготовки метода вставьте изменения так, чтобы хеш-коды равных объектов совпадали.

Совет. Так как вы вряд-ли захотите писать код хеш-функции (см. Кнут Д. Искусство программирования, какой-то из томов. 1999), то перепоручите задачу генерации кодов самой системе. Она умеет это делать лучше нас. Дело в том, что встроенные типы данных уже имеют переопределенные версии GetHashCode, которые используют грамотное хеширование. Рекомендую поэкспериментировать и опробовать различные версии алгоритма образования хеш-кода с тем, чтобы лучше понять, что при этом делает система. Манипулируйте тем, что есть у Man'а.

Документация MSDN не рекомендует переопределять смысл операций == и !=. По умолчанию для объектов типа Reference они проверяют идентичность ссылок (адресов). Пусть они этим и занимаются. Для проверки идентичности данных в классе Object существует метод Equals. Мы его уже переопределили. Уберите из класса Man переопределение операций == и !=. Они были введены с учебными целями—показать как это делается в принципе.

Реализация интерфейса

Заглянув в MSDN, мы узнаём, что, кроме уже опробованных методов (IndexOf, и т.д.) массив имеет метод Sort, который, конечно же, представляет практический интерес. Но на данной стадии развития класса Man мы не сможем воспользоваться им для упорядочивания массива. Если мы попробуем это сделать, то возникнет ошибка на этапе выполнения (исключительная ситуация), которое означает, что для функционирования метода Sort класс сортируемых объектов должен реализовать интерфейс IComparer (или IComparable).

Интерфейс заявляет свои методы, но не реализует их. Он лишь определяет правила игры, по которым должен играть класс, решивший предоставить миру (expose) ту функциональность, которой обладает интерфейс. Для нас это означает, что класс Man должен быть усовершенствован. Он должен объявить миру, что он реализует (implements) интерфейс IComparable, то есть становится цивилизованным классом, готовым играть по общепринятым правилам. Отныне людей можно будет сравнивать, используя ту же логику (методы интерфейса задают именно логику), которая используется для сравнения объектов всех других (цивилизованных) типов данных, например string или int и т.д. Более того, наш класс должен на деле выполнить заявку (осуществить обещание). Это означает, что он должен ввести в свой состав метод IComparable.CompareTo и реализовать (implement) его тело. В том подходе, который реализует C#, это делается путем наследования.

Интерфейс IComparable имеет всего лишь один метод CompareTo, который возвращает результат сравнения двух объектов. Речь идет об объектах того класса, который обещал реализовать интерфейс. Возвращаемое значение определяется почти так же, как и в известной функции языка С strcmp, которая сравнивает две строки текста и определяет их лексикографический порядок. Вот этот алгоритм:

·  Если первый аргумент меньше второго, то возвращается целое число, которое меньше нуля,

·  Если аргументы равны, то возвращается ноль, и, наконец,