Обобщения. Ограниченные типы. Общие понятия. Ограничения ссылочного и структурного типов
Содержание
- 1. Понятие об ограниченных типах в обобщениях. Случаи применения ограничений. Перечень возможных ограничений
- 2. Ограничение ссылочного типа: where T : class
- 3. Пример, объясняющий использование ограничения ссылочного типа class
- 4. Ограничение типа where T : struct. Особенности использования
- 5. Пример, объясняющий использование ограничения типа struct
- Связанные темы
Поиск на других ресурсах:
1. Понятие об ограниченных типах в обобщениях. Случаи применения ограничений. Перечень возможных ограничений
При использовании обобщений элемент программы (класс, структура, интерфейс, …) получает параметром некоторый обобщенный тип T и использует его для реализации решении задачи. Язык C# позволяет задать ограничения для параметра типа T. Эти ограничения определяют требования, которым должен отвечать тип данных T.
Ограничения типов необходимы в следующих случаях:
- если программист создает собственные классы или собственную иерархию классов, выступающих параметрами типа T и только эти классы должны выступать аргументами типа. В этом случае задается имя класса или имя базового класса в иерархии. Другими словами, когда нужно гарантировать, что в качестве аргументов типа T будет избрано строго определенное множество классов в указанной иерархии;
- когда нужно сообщить компилятор о том, что нужно использовать методы (свойства) строго определенных типов (классов, структур и т.д.).
Общая форма объявления ограничения для типа T следующая:
where T : bounds
здесь
- bounds – ограничения, которые накладываются на использование типа T.
Ниже приведен перечень возможных ограничений, которые может получать bounds:
- where T: struct — ограничение типа-значения. Параметр типа должен быть унаследован от System.ValueType, то есть быть структурным типом;
- where T: class — ограничение типа-ссылки. Параметр типа должен быть ссылочного типа, то есть не должен быть унаследован от System.ValueType;
- where T: new() — параметр типа должен иметь конструктор без параметров (конструктор по умолчанию);
- where T: BaseClass — параметр типа должен быть классом BaseClass или классом, производным от него;
- where T: Interface — параметр типа должен реализовывать интерфейс с именем Interface.
Допускается указывать нескольких видов ограничений одновременно. В этом случае перечень ограничений задается через запятую, например
where T : class, IEquatable, new()
Здесь задаются одновременно 3 ограничения:
- аргумент типа T должен быть ссылочного типа (class);
- аргумент типа T должен реализовывать интерфейс IEquatable;
- аргумент типа T должен содержать конструктор по умолчанию — new().
⇑
2. Ограничение ссылочного типа: where T : class
Ограничение ссылочного типа применется, когда нужно запретить использование значимых типов (int, float, double и т.д.) в качестве аргумента типа. Более подробно об особенностях ссылочных типов описывается здесь.
В этом случае ограничение аргумента типа T может быть:
- любым классом из стандартной библиотеки .NET Framework или классом сторонних разработчиков;
- любым собственно-разработанным классом программы.
⇑
3. Пример, объясняющий использование ограничения ссылочного типа class
Пусть задан класс RefTypes<T>, в котором тип T ограничивается ключевым словом class
// Класс, в котором тип T может быть только ссылочным типом class RefTypes<T> where T : class { // ... }
Это означает, что аргументом типа T может быть любой ссылочный тип из библиотеки .NET, например, String или System.IO.BinaryReader
// работает, поскольку тип String - ссылочный тип RefTypes<String> ref1 = new RefTypes<String>(); // работает, поскольку тип System.IO.BinaryReader - ссылочный тип RefTypes<System.IO.BinaryReader> ref3 = new RefTypes<System.IO.BinaryReader>();
Также аргументом типа T может быть любой тип, реализованный в программе. Если в программе объявляется класс, интерфейс и структура
// Пользовательский класс в программе class MyClass { // ... } // Пользовательский интерфейс interface MyInterface { void Print(); } // Пользовательская структура в программе struct MyStruct { // ... }
то создать экземпляр класса с аргументами типов MyClass и MyInterface допускается, поскольку они относятся к аргументам ссылочного типа
// работает, поскольку MyClass - собственно-разработанный класс RefTypes<MyClass> obj = new RefTypes<MyClass>(); // работает, поскольку MyInterface - ссылочного типа RefTypes<MyInterface> mi = new RefTypes<MyInterface>();
А вот при попытке создания экземпляра типа MyStruct или int (или другого типа-значения) компилятор выдаст ошибку
// Попытка создать экземпляр класса RefTypes с аргументом типа int RefTypes<int> rt1 = new RefTypes<int>(); // ошибка компиляции RefTypes<Int32> rt2 = new RefTypes<int>(); // также ошибка компиляции // ошибка компиляции, поскольку MyStruct - это структура, // относящаяся к типам значениям RefTypes<MyStruct> ms = new RefTypes<MyStruct>();
⇑
4. Ограничение типа where T : struct. Особенности использования
Ограничения значимых типов struct используются для запрета использования ссылочных типов в качестве аргументов типа. Более подробно о типах-значения и ссылочных типах описывается здесь.
Это ограничение противоположно ограничению типа class (смотрите предыдущие пункты). В ограничении типа struct разрешается использовать следующие типы:
- структурные типы такие как int, float, char, double и другие а также их синонимы Int32, Boolean, Char, Single, Double и другие;
- пользовательские структурные типы (структуры), которые объявляются с использованием ключевого слова struct.
⇑
5. Пример, объясняющий использование ограничения типа struct
Пусть задано следующее объявление класса ValTypes<T> в котором обобщенный тип T ограничен ключевым словом struct
// Класc, в котором тип T может быть только типом-значения class ValTypes<T> where T : struct { // ... }
При таком объявлении допускается объявлять экземпляры класса ValTypes<T>, в котором аргументами типа выступает любой значимый тип, например int или double
// Объявление экземпляров для типа int (Int32) ValTypes<int> obj1 = new ValTypes<int>(); ValTypes<Int32> obj2 = new ValTypes<int>(); // Объявление экземпляров для типа double (Double) ValTypes<double> obj3 = new ValTypes<double>(); ValTypes<Double> obj4 = new ValTypes<Double>();
Это объясняется тем, что типы int, double, float и другие, а также их синонимы (Int32, Double, Single …) относятся к значимым типам – они являются структурами.
Также можно объявлять экземпляры пользовательских структур или структур из библиотеки .NET Framework.
Если в программе объявляется структура MyStruct
// Пользовательская структура в программе struct MyStruct { // ... }
то тип этой структуры может быть применен как аргумент типа T при объявлении экземпляра ValTypes<T>
// Объявление экземпляра ValueTypes<T> с аргументом типа, // которым выступает структура MyStruct ValTypes<MyStruct> ms = new ValTypes<MyStruct>();
Однако, если попытаться объявить экземпляр класса ValTypes<T> с любым ссылочным типом, то возникнет ошибка на этапе компиляции
// Пользовательский класс в программе class MyClass { // ... } ... // Ошибка на этапе компиляции ValTypes<MyClass> obj = new ValTypes<MyClass>();
То же касается и классов из библиотеки .NET Framework
// Класс String есть ссылочного типа ValTypes<String> obj = new ValTypes<String>(); // ошибка компиляции // Класс StreamReader также ссылочного типа ValTypes<System.IO.TextReader> obj = new ValTypes<System.IO.TextReader>(); // ошибка компиляции
Такую реакцию компилятора обеспечивает ограничение struct при объявлении класса ValTypes<T>.
⇑
Связанные темы
- Обобщения. Основные понятия. Обобщенные классы и структуры
- Ограничения на конструктор, базовый класс и интерфейс
- Ограничения в обобщенных методах и делегатах. Примеры. Применение ограничений для несколько типов
⇑