Класс 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;
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.