Класс-прототип (версия 2.0) Обычный класс

Comparer<T> Comparer

Dictionary<K.T> HashTable

LinkedList<T> -

List<T> ArrayList

Queue<T> Queue

SortedDictionary<K.T> SortedList

Stack<T> Stack

У коллекций, описанных в библиотеке NET версий 1.0 и 1.1, есть два основных недостатка, обусловленных тем, что в них хранятся ссылки на тип object:

□ в одной и той же коллекции можно хранить элементы любого типа, следовательно, ошибки при помещении в коллекцию невозможно проконтролировать на этапе компиляции, а при извлечении элемента требуется его явное преобразование;

□ при храпении в коллекции элементов значимых типов выполняется большой
объем действий по упаковке и распаковке элементов, что в значительной сте­пени снижает эффективность работы.

Параметром класса-прототипа является тип данных, с которым он работает. Это избавляет от перечисленных недостатков. В качестве примера рассмотрим при­менение универсального «двойника» класса ArrayList - класса List<T> - для хранения коллекции объектов классов Monster и Daemon, которые разрабатывались в главах 5 и 8, а также для хранения целых чисел1.

Листинг 13.2. Использование универсальной коллекции List<T>

using System;

using System.ColIections.Geneneriс;

using System.Text;

namespace ConsoleApplication1

{

using MonsterLib;

class Program

{

static void Main()

{

List<Monster> stado = new List<Monster>();

stado.Add(new Monster("Monia"));

stado.Add(new Monster("Monk"));

stado.Add(new Daemon("Dimon", 3));

foreach (Monster x in stado)

x.Passport();

List<int> lint = new List<int>();

lint.Add(5);

lint.Add(1);

lint.Add(3);

lint.Sort();

int a = lint[2];

Console.Writel_ine(a);

foreach (int x in lint)

Console.Write(x + " ");

}

}

}

Результат работы программы:

Monster Monia health = 100 ammo - 100

Monster Monk health - 100 ammo = 100

Daemon Dimon health = 100 ammo = 100 brain - 3

1 3 5

В листинге 13.2 две коллекции. Первая (stado) содержит элементы пользователь­ских классов, которые находятся в библиотеке MonsterLib.dll, созданной в преды­дущей главе. В коллекции, для которой объявлен тип элементов Monster, благода­ря полиморфизму можно хранить элементы любого производного класса, но не элементы других типов.

Казалось бы, по сравнению с обычными коллекциями это ограничение, а не уни­версальность, однако на практике коллекции, в которых действительно требует­ся хранить значения различных, не связанных межу собой типов, почти не ис­пользуются. Достоинством же такого ограничения является то, что компилятор может выполнить контроль типов во время компиляции, а не выполнения про­граммы, что повышает ее надежность и упрощает поиск ошибок. Коллекция lint состоит из целых чисел, причем для работы с ними не требуются ни операции упаковки и распаковки, ни явные преобразования типа при получении элемента из коллекции, как это было в обычных коллекциях (см. листинг 13.1). Классы-прототипы называют также родовыми или шаблонными, поскольку они представляют собой образцы, по которым во время выполнения программы строятся конкретные классы. При этом сведения о классах, которые являются параметрами классов-прототипов, извлекаются из метаданных.

ВНИМАНИЕ

Использование стандартных параметризованных коллекций для хранения и обра­ботки данных является хорошим стилем программирования, поскольку позволяет сократить сроки разработки программ и повысить их надежность. Рекомендуется тщательно изучить по документации свойства и методы этих классов и выбирать наиболее подходящие в зависимости от решаемой задачи.

В листинге 13.3 приведен еще один пример применения параметризованных коллекций. Программа считывает содержимое текстового файла, разбивает его на слова и подсчитывает количество повторений каждого слова в тексте. Для хранения слов и числа их повторений используется словарь Dictionary<T.K>. У этого класса два параметра: тип ключей и тип значений, хранимых в словаре. В качестве ключей используются слова, считанные из файла, а значения пред­ставляют собой счетчики целого типа, которые увеличиваются на единицу, когда слово встречается в очередной раз.

Листинг 13.3. Формирование частотного словаря

using System;

using System.Collections.Generiс;

using System.Text;

using System.IO;

namespace ConsoleApplication1

{

class Program

{

static void Main()

{

StreamReader f = new StreamReader(@"d:\C#\text.txt"); // 1

string s = f.ReadToEnd(); // 2

char[] separators = { '.', ' ', '.', '!' }; //3

List<string> words = new List<string>(s.Split(separators)); // 4

Dictionary<string, int> map = new Dictionary<string, int>(); // 5

foreach (string w in words) // 6

{

if (map.ContainsKey(w)) map[w]++;

else map[w] = 1;

}

foreach (string w in map.Keys) // 7

Console.WriteLine("{0}\t{1}", w.map[w]);

}

}

}

Пусть исходный файл text.txt содержит строки

Ехал Грека через реку. Видит Грека, в реке рак.

Сунул Грека в реку руку, рак за руку Греку цап!

Тогда результат работы программы будет выглядеть так:

Ехал 1

Грека 3

через 1

реку 2
4

Видит 1

в 2

реке 1

рак 2

Сунул 1

руку 2

за 1

Греку 1

цап 1

Несколько пояснений к программе. В операторе 1 открывается текстовый файл, длина которого не должна превышать 32 767 символов, потому что в операторе 2 все его содержимое считывается в отдельную строку.

ПРИМЕЧАНИЕ

Конечно, для реальной работы такой способ не рекомендуется. Кроме того, для файлов, открываемых для чтения, программа обязательно должна обрабатывать ис­ключение FileNotFoundException (см. главу 11).

В операторе 3 задается массив разделителей, передаваемый в качествепараметра методу Split, формирующему массив строк, каждая из которых содержит отдель­ное слово исходного файла. Этот массив используется для инициализации эк­земпляра words класса List<string>. Применение стандартного класса позволяет не заботиться о выделении места под массив слов.

Оператор 5 описывает словарь, а в цикле 6 выполняется его заполнение путем просмотра списка слов words. Если слово встречается впервые, в значение, соот­ветствующее слову как ключу, заносится единица. Если слово уже встречалось, значение увеличивается на единицу.

В цикле 7 выполняется вывод словаря путем просмотра всех его ключей (для этого используется свойство словаря Keys, возвращающее коллекцию ключей) и вы­борки соответствующих значений.

Метод Split не очень интеллектуален: он рассматривает пробел, расположенный после знака препинания, как отдельное слово. Для более точного разбиения на сло­ва используются регулярные выражения, которые рассматриваются в главе 15.

ПРИМЕЧАНИЕ-

Обратите внимание на то, насколько использование стандартных коллекцийсокра­щает исходный текст программы. Конечно, на тщательное изучение их возможно­стей требуется много времени, однако это окупается многократно.

Для полноты картины следует добавить, что наряду с параметризованными клас­сами в пространстве имен System.Collections.Generic описаны параметризованные интерфейсы, перечисленные в табл. 13.6.

Таблица 13.6. Параметризованные интерфейсы библиотеки.NET версии 2.0

Параметризованный интерфейс (версия 2.0) Обычный интерфейс

ICollection<T> ICollection

IComparable<T> IComparable

IDictionary<K.T> IDictionary

IEnumerable<T> IEnumerable

IEnumerator<T> IEnumerator

IList<T> IList


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: