Исследование MS Visual С#, страница 8

1.  Доберитесь до узла Object в дереве класса Man (так, как вы это недавно делали для функции Equals) и раскройте его.

2.  Среди методов класса Object найдите функцию с именем GetHashCode(), вызовите контекстное меню над ее именем и дайте команду Add4Override.

3.  В тело заготовки вставьте изменения так, чтобы хеш-коды равных объектов совпадали.

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

Здесь я рекомендую поэкспериментировать и опробовать различные версии алгоритма образования хеш-кода с тем, чтобы лучше понять, что при этом делает система. Манипулируйте тем, что есть у Man'а. Отметьте также, что в одном из окон отладчика, например, Watch1, мы можем увидеть значение, возвращаемое методом класса (например, men[0].GetHashCode()).

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

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

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

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

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

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

¨  Если первый аргумент больше второго, то возвращается положительное число.

Теперь, когда стало ясно, что от нас требуется, внесите изменения в класс Man. Он должен происходить от интерфейса IComparable и иметь метод CompareTo.

class Man : IComparable

{

  //==== Здесь объявление данных и методов класса Man

int IComparable.CompareTo(object other)      // Новый метод, реализующий интерфейс

{

  return // Сюда вставьте нужный код

}

}

В теле метода не надо изобретать ничего нового. Пользуйтесь готовым решением. Класс string, объектом которого является поле данных name, уже реализует интерфейс IComparable, поэтому перепоручите ему сравнение имен двух объектов. Этого достаточно для того, чтобы включить возможность сортировки массива по имени. Опробуйте новую функциональность.

static void Main()

{

//==== Создайте массив

Console.WriteLine("Men before Sort:\n");

  //==== Выведите массив

Array.Sort(men); // Для сортировки пользуемся статическим методом класса Array

Console.WriteLine("\n\nMen after Sort:\n");

  //==== Выведите массив

}

Метод Sort объявлен в классе Array как статический, поэтому мы его вызываем по имени класса, а не с помощью объекта. Попробуйте ответить на такие вопросы.

¨  Что следует сделать, чтобы объекты можно было отсортировать по возрасту, а не по имени?

¨  Что следует сделать, чтобы дать пользователю возможность изменять способ сортировки?

Перечисления (enum)

Способ или режим сортировки объектов класса (по имени или по возрасту), очевидно, должен быть характеристикой всего массива объектов, а не каждого объекта в отдельности. В то же время он должен быть инкапсулирован в классе Man, так как массивы объектов другого типа могут и не иметь полей name и age. Вы уже догадались, что я подвожу вас к мысли о необходимости ввести в класс Man статическую переменную, задающую способ сортировки. Статические переменные являются общими для всех объектов класса и существуют даже если нет ни одного объекта класса.

Так как количество способов сортировки ограничено, то для хранения текущего способа целесообразно ввести новый тип данных — перечисление (enum). Он значительно более информативен, чем какой-либо другой целый тип. Введите в пространство имен ManSpace новый тип, который, кроме двух очевидных значений (eName, eAge), содержит еще два, которые понадобятся нам позже. Кроме того, в класс Man введите объявление новой переменной рассматриваемого типа.

namespace ManSpace

{

enum eSortBy : byte { eNone, eName, eAge, eStatus }; // Новый тип данных

//=== Здесь следуют определения других классов

class Man : IComparable

{

  //=== Здесь данные  класса Man

  public static eSortBy sortBy = eSortBy.eName; // Статическая переменная

  //=== Здесь следуют методы класса Man