Универсальные классы (Руководство по программированию на C#)
Универсальные классы инкапсулируют операции, не относящиеся к какому-либо определенному типу данных.Чаще всего универсальные классы используются с коллекциями, такими как связанные списки, хэш-таблицы, очереди, стеки, деревья и т.п.Такие операции как добавление элементов в коллекцию или их удаление осуществляются одинаково вне зависимости от типа хранящихся данных.
Для большей части сценариев, в которых требуются классы коллекций, рекомендуется использовать классы, входящие в библиотеку классов платформы .NET Framework.Дополнительные сведения об использовании этих классов см. в разделе Универсальные шаблоны в библиотеке классов платформы .NET Framework (Руководство по программированию в C#).
Как правило, создания универсальных классов начинается с запуска существующего класса и изменения типов на параметры типов по одному до тех пор, пока не будет достигнуто оптимальное соотношение между универсальностью и удобством.При создании собственных универсальных классов следует учитывать следующее:
Какие типы преобразовывать в параметры.
Как правило, чем больше типов преобразовано в параметры, тем более гибким становится программный код.Однако излишняя универсальность использование ключевого слова может сделать код более трудным для понимания другими разработчиками.
Какие ограничения применяются к параметрам типов (см. Ограничения параметров типа (Руководство по программированию на C#)).
Рекомендуется применять наибольшее возможное число ограничений, при котором обеспечивается обработка нужных типов.Например, если известно, что универсальных класс нужен только для ссылочных типов, можно применить ограничение класса.Это предотвратит использования класса с типами значений и позволит использовать оператор as для T и проверять нулевые значения.
Следует ли разделять поведение универсального класса на базовые классы и подклассы.
Универсальные классы могут служить базовыми классами, поэтому при их создании следует учитывать такие же обстоятельства, как при создании классов, не являющихся универсальными.См. правила наследования от универсальных базовых классов далее в этом разделе.
Следует ли реализовывать один или несколько универсальных интерфейсов.
Например, при создании класса, который будет использован для создания элементов в универсальной коллекции, может понадобиться реализовать интерфейс, подобный IComparable<T>, где T — тип вашего класса.
Пример простого универсального класса см. в разделе Введение в универсальные шаблоны. (Руководство по программированию на C#).
В правилах, действующих для параметров типов и ограничений, действует несколько неявных принципов поведения универсальных классов, особенно в отношении наследования и доступа к членам.Перед тем, как продолжить, следует уяснить некоторые моменты.Для универсального класса Node<T>, клиентский код может ссылаться на класс путем указания аргумента типа, чтобы создать закрытый тип (Node<int>).Также можно не указывать параметр типа, чтобы создать открытый тип (Node<T>).Универсальные классы могут быть унаследованы от конкретных закрытых или открытых базовых классов.
class BaseNode { }
class BaseNodeGeneric<T> { }
// concrete type
class NodeConcrete<T> : BaseNode { }
//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }
//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }
Классы, не являющиеся универсальными, то есть конкретные классы, могут быть унаследованы от закрытых базовых классов, но не от открытых базовых классов и от параметров типов, поскольку во время выполнения клиентский код не может предоставить аргумент типа, необходимый для создания экземпляра базового класса.
//No error
class Node1 : BaseNodeGeneric<int> { }
//Generates an error
//class Node2 : BaseNodeGeneric<T> {}
//Generates an error
//class Node3 : T {}
Универсальные классы, унаследованные от открытых базовых классов, должны предоставлять аргументы типа для всех параметров типа базового класса, к которым нет доступа у наследующего класса, как показано в следующем примере кода.
class BaseNodeMultiple<T, U> { }
//No error
class Node4<T> : BaseNodeMultiple<T, int> { }
//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }
//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {}
Универсальные классы, унаследованные от открытых базовых классов, должны указывать ограничения, которые соответствуют ограничениям базового типа или являются более строгими.
class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }
Универсальные типы могут использовать несколько параметров типа и ограничений.
class SuperKeyType<K, V, U>
where U : System.IComparable<U>
where V : new()
{ }
Открытые и закрытые типы можно использовать в качестве параметров методов.
void Swap<T>(List<T> list1, List<T> list2)
{
//code to swap items
}
void Swap(List<int> list1, List<int> list2)
{
//code to swap items
}
Если универсальных класс реализует интерфейс, все экземпляры этого класса могут быть приведены к этому интерфейсу.
Универсальные классы инвариантны.Другими словами, если входной параметр указывает List<BaseClass>, при попытке предоставить List<DerivedClass> возникнет ошибка компиляции.
См. также
Ссылки
Универсальные шаблоны (Руководство по программированию на C#)
Основные понятия
Руководство по программированию на C#