Узагальнення. Обмежені типи. Загальні поняття. Обмеження посилального та значимого типів
Зміст
- 1. Поняття про обмежені типи в узагальненнях. Випадки застосування обмежень. Ключове слово where. Перелік можливих обмежень
- 2. Обмеження посилального типу: where T : class
- 3. Приклад, що пояснює використання обмеження посилального типу class
- 4. Обмеження типу where T : struct. Особливості використання
- 5. Приклад, що пояснює використання обмеження типу struct
- Зв’язані теми
Пошук на інших ресурсах:
1. Поняття про обмежені типи в узагальненнях. Випадки застосування обмежень. Ключове слово where. Перелік можливих обмежень
При використанні узагальнень елемент програми (клас, структура, інтерфейс, …) отримує параметром деякий узагальнений тип 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
// Клас, в якому тип 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>.
⇑
Зв’язані теми
- Узагальнення. Основні поняття. Узагальнені класи та структури
- Обмеження на конструктор, базовий клас та інтерфейс
- Обмеження в узагальнених методах та делегатах. Приклади. Застосування обмежень для декількох типів
⇑