Преимущества универсальных шаблонов (Руководство по программированию на C#)

Обновлен: Ноябрь 2007

Универсальные шаблоны предоставляют решение для ограничения более ранних версий среды CLR и языка C#, в которых обобщения достигались за счет приведения типов к универсальному базовому типу Object и из него. Путем создания универсального класса можно создать коллекцию, которая была бы строго типизирована во время компиляции.

Ограничения, возникающие при использовании неуниверсальных классов коллекции, можно продемонстрировать на примере написания короткой программы, использующей класс коллекции ArrayList из библиотеки классов платформы .NET Framework. ArrayList — это очень удобный класс коллекции, который можно использовать без изменений для хранения типа значения и ссылки на него.

// The .NET Framework 1.1 way to create a list:
System.Collections.ArrayList list1 = new System.Collections.ArrayList();
list1.Add(3);
list1.Add(105);

System.Collections.ArrayList list2 = new System.Collections.ArrayList();
list2.Add("It is raining in Redmond.");
list2.Add("It is snowing in the mountains.");

Однако за удобство приходится платить. Любые ссылки или типы значения, добавляемые к классу коллекции ArrayList, неявно приводятся к Object. Если в качестве элементов выступают типы значений, то для них необходимо выполнить упаковку-преобразование при добавлении к списку и распаковку-преобразование при извлечении из списка. Операции приведения, упаковки-преобразования и распаковки-преобразования снижают производительность. Влияние операций упаковки-преобразования и распаковки-преобразования в сценариях обработки больших коллекций может быть весьма значительным.

Еще одно ограничение состоит в отсутствии проверки типа во время компиляции. Поскольку класс коллекции ArrayList приводит все к типу Object, невозможно предотвратить выполнение клиентским кодом во время компиляции действий, подобных следующим:

System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");

int t = 0;
// This causes an InvalidCastException to be returned.
foreach (int x in list)
{
    t += x;
}

Несмотря на то, что комбинирование строк и ints в единый класс коллекции ArrayList вполне допустимо, а иногда и специально выполняется (при создании разнородной коллекции), во многих случаях это может привести к ошибке программы, которую можно будет обнаружить только при выполнении.

В версиях языка C# 1.0 и 1.1 можно избежать неприятностей, вызванных использованием обобщенного кода в классах коллекции библиотеки базового класса платформы .NET Framework только путем написания пользовательских коллекций особого типа. Конечно, пользователь теряет преимущества обобщения из-за того, что такие классы можно использовать только для одного типа данных, и необходимо переписывать класс для каждого типа сохраняемых данных.

Что действительно необходимо для класса коллекции ArrayList и других подобных классов — это возможность указать для каждого отдельного экземпляра в клиентском коде определенный тип данных, который они должны использовать. Это устранит необходимость приведения к типу T:System.Object и даст компилятору возможность проверить тип. Другими словами, классу коллекции ArrayList необходим параметр типа. Это как раз то, что имеется в универсальном шаблоне. В универсальном шаблоне коллекции List<T> в пространстве имен N:System.Collections.Generic та же операция добавления элементов в коллекцию выглядит следующим образом:

// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();

// No boxing, no casting:
list1.Add(3);

// Compile-time error:
// list1.Add("It is raining in Redmond.");

Единственным различием в клиентском коде между List<T> и ArrayList является аргумент типа в разделе объявления и создания экземпляра. Благодаря этому небольшому усложнению кода можно создать список, который будет не только безопаснее, чем ArrayList, но и существенно быстрее, особенно если элементами списка являются типы значений.

См. также

Основные понятия

Руководство по программированию в C#

Ссылки

Введение в универсальные шаблоны. (Руководство по программированию на C#)

System.Collections.Generic

Упаковка-преобразование и распаковка-преобразование (Руководство по программированию на C#)

Другие ресурсы

Рекомендации по коллекциям