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

¨  IndexOf — возвращает индекс первого искомого элемента в массиве;

¨  LastIndexOf — возвращает индекс последнего искомого элемента в массиве;

¨  Reverse — реверсирует (переворачивает) массив;

¨  Equals — проверяет равенство двух ссылок;

¨  Copy — копируeт подпоследовательность.

Вы можете начаать со следующей заготовки, которая быстро создает испытуемый массив.

static void Main()

{

Man   // Три ссылки на отдельные объекты

  ken = new Man("Ken Wood",40),

  len = new Man("Lennie Tristano",50),

  ben = new Man("Ben Webster",60);

Man[] men = { ken, len, ben, ken, len, ben };   // Массив ссылок на объекты

  //===== Поиск первого вхождения (Вставьте код)

  //===== Поиск последнего вхождения (Вставьте код)

Console.WriteLine("The last Lennie's id = {0}",id);

  //===== Проверка равенства ссылок (?)

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

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

//===== Реверс последовательности (Вставьте код)

  Console.WriteLine("\nAfter Reverse:\n");

foreach (Man m in men)

  m.Out();

//===== Копирование подпоследовательности (Вставьте код)

Console.WriteLine("\nAfter Copy:\n");

  // . . .       foreach (

}

Метод Array.Equals возвращает булевский результат проверки равенства двух ссылок на объекты и в нашем случае дает осмысленные результаты, но если копнуть глубже, то мы увидим, что он работает не так, как было бы естественно предположить. Создайте новый модуль (старый вас запутает) и исследуйте такой вариант:

Man ken = new Man ("Ken Wood",40);

Man[] men = { ken, ken, ken };

Console.WriteLine("ken == men[0] = {0}",Array.Equals(ken,men[0])); // Равны ?

men[0] = new Man ("Ken Wood",40);

Console.WriteLine("ken == men[0] = {0}",Array.Equals(ken,men[0]));// Равны ?

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

Логичны ли выведенные результаты? Почему они такие?

Определение бинарных операций

Чтобы выправить ситуацию, следует переопределить в классе Man виртуальную функцию Equals, доставшуюся в наследство от класса Object. Рекомендую сделать это с помощью студии.

1.  Переведите фокус мыши на узел Man в дереве классов окна Class View и раскройте этот узел.

2.  Найдите и раскройте узел Bases and Interfaces, а затем (внутри него) узел Object.

3.  Найдите ветвь дерева унаследованных методов с именем Equals(object), вызовите контекстное меню и дайте команду Add4Override.

В классе Man появится заготовка переопределенной версии функции Equals:

public override bool Equals(object obj)

{

return true;

}

В теле этой функции надо сравнить два объекта класса Man и вернуть результат булевского типа. Какие два объекта сравнивать? Вспомните концепции ООП и сравните объект, которому будет послано сообщение Equals, с тем объектом, который задан параметром функции Equals. Он имеет тип ссылки на родительский класс object. Вспомните правило ООП: «родители могут указывать на детей, но дети не могут указывать на родителей» и измените код функции Equals так, чтобы она возвращала true только если два объекта идентичны (равны по сути, а не по адресу).

Заодно покажем как переопределить operator== в C#. В отличие от С++, где есть некоторая свобода при выборе способа переопределения бинарной операции (метод с 1-м параметром или friend-функция с 2-мя параметрами), в C# возможен только один способ — статический метод с двумя параметрами. Это устраняет произвол и делает язык более строгим.

Следуя этому правилу, введите определение операции operator==, в теле которой используйте вызов уже созданной вами версии метода Equals. После этого запустите приложение. Оно должно работать как и предполагалось, то есть объекты будут сравниваться, опираясь на их содержимое, а не значение их адресов. Последнее сравнение должно дать истину (ken == men[0] = True). Но в окне Output появятся два предупреждения:

              warning CS0659: 'ManSpace.Man' overrides Object.Equals(object) but does not override Object.GetHashCode()

              warning CS0661: 'ManSpace.Man' defines operator == or operator != but does not override Object.GetHashCode()

которые означают, что нехорошо переопределять Equals или operator==, но не трогать при этом виртуальную функцию GetHashCode, которая используется при работе с коллекциями объектов.

Метод GetHashCode

Несмотря на то, что мы пока не пользуемся коллекцией типа Hashtable, тем не менее наши объекты уже могут получить свои ключи, так как функция GetHashCode унаследована от класса Object и доступна для наших объектов. Вы можете увидеть какие значения будут сопоставлены объектам класса Man. Для этого вставьте в функцию Main такой фрагмент:

Console.Write("\nHash keys: ");

foreach (Man m in men) // Пользуемся родительской версией функции GetHashCode

Console.Write("{0}, ", m.GetHashCode());

Console.WriteLine("\n");

Запустите и убедитесь, что ссылкам на одни и те же объекты поставлены в соответствие одинаковые ключи. Основным принципом использования хеш-таблиц является то, что одинаковым объектам соответствуют одинаковые ключи. Удовлетворяется ли этот принцип в нашем случае?

Выведите ключи для клонированного объекта men[0] и его «близнеца» ken (сразу после создания клона). Теперь вы должны понять смысл предупреждений компилятора. В нашем алгоритме как операция Equals, так и operator==() считают, что объекты ken и men[0] равны, однако функция GetHashCode возвращает для них разные коды ключей. Этот диссонанс скажется (приведет к алгоритмическим ошибкам) когда мы будем пользоваться коллекцией типа Hashtable. Давайте с помощью студии устраним этот недостаток.