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

Класс Hashtable является мощным инструментом, с помощью которого можно эффективно и надежно управлять сложными структурами  данных. Для того, чтобы этот инструмент успешно работал, необходимо выполнить некоторые требования. Например, класс объектов, используемых в качестве ключа, должен реализовывать или наследовать методы: GetHashCode и Equals с учетом особенностей, которые были рассмотрены ранее. Напомним, что двум равным объектам должны соответствовать одинаковые хеш-коды, которые возвращает метод GetHash класса Hashtable. По умолчанию он вызывает тот самый метод GetHashCode, который мы ввели в класс Man.

Оператор цикла foreach может быть использован для пробега по коллекции типа Hashtable. Этот оператор, как мы уже отмечали, самостоятельно осуществляет приведение типов (например, от типа object к типу Man) и, поэтому, он должен знать тип элементов в коллекции. Интересно, что в Hashtable хранятся пары, составляющие которой могут быть разных типов. Поэтому нельзя определенным образом ответить на вопрос какого типа элементы хранятся в Hashtable. Например, key может иметь тип ссылки на объект пользовательского класса, а value — любой другой тип. Для того, чтобы выйти из положения разработчики создали специальную структуру (тип элементов) DictionaryEntry. Для перебора элементов коллекции в цикле foreach вы должны использовать именно эту структуру.

foreach (DictionaryEntry e in myHashtable)

{

   // Делаем что-то с каждым объектом e

}

Продемонстрируем как создать коллекцию пар такого типа и как ей управлять. Простейшим примером является коллекция пар (человек, профессия).

Hashtable ht = new Hashtable();

ht.Add (ken, "Writer");

ht.Add (len, "Pianist");

ht.Add (ben, "Sax player");

foreach (DictionaryEntry e in ht)

  Console.WriteLine( "{0}  -  {1}", e.Key, e.Value);

Этого кода достаточно для функционирования коллекции. Несмотря на то, что мы не давали никаких указаний относительно того, что будет храниться в коллекции, объект ht класса Hashtable прекрасно справился с той задачей, которую ему задали. Это можно объяснить тем, что на самом деле он хранит пары типа (адрес одного объекта, адрес другого объекта).

Порядок прохода по элементам коллекции в цикле вывода оказался обратным тому, который был использован при вводе элементов, так как ассоциативные контейнеры не гарантируют поддержку какого-то упорядочивания своих элементов, хотя они и дают возможность последовательного прохода по ним. Изменять существующие элементы коллекции можно, обращаясь к ним с помощью операции выбора []. Вы можете проверить это, вставив до цикла foreach такую строку:

ht[ken] = "Movie maker";

Далее, вы можете вставить в конец существующего кода такой фрагмент:

ht.Clear(); // Удаляем все элементы

ht.Add ("Peter", 40000);  // Вставляем другие пары

ht.Add ("Andrew", 45000);

ht.Add ("Simon", 25000);

foreach (DictionaryEntry e in ht)

Console.WriteLine( "{0}  earns  {1}", e.Key, e.Value);

Удивительным кажется тот факт, что из приведенного кода можно убрать строку ht.Clear(); и, как ни в чем не бывало, продолжать работать, с уже неоднородной коллекцией пар. В ней будут присутствовать все пары, вставленные до этого момента. Тип элемента текущей пары можно выяснять на ходу. Такой сценарий реализован в следующей версии функции Main.

Hashtable ht = new Hashtable();

ht.Add (ken, "Writer"); // Вставляем пары типа (Человек-Профессия)

ht.Add (len, "Pianist");

ht.Add (ben, "Sax player");

ht[ken] = "Movie maker"; // Изменяем второй элемент пары, обращаясь к нему с помощью индекса (ключа)

Console.WriteLine ("\nTest change: {0}\n",ht[ken]);

ht.Add ("Private", 1);    ht.Add ("Corporal", 2);       // Вставляем совсем другие пары

ht.Add ("Sergeant", 3); ht.Add ("Lieutenant", 4);

ht.Add ("Captain", 5);    ht.Add (100, "My Debt");

int nMen = 0, nStrings = 0;