C#. Обобщения. Ограниченные типы. Общие понятия

Обобщения. Ограниченные типы. Общие понятия. Ограничения ссылочного и структурного типов


Содержание


Поиск на других ресурсах:




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>.

 


Связанные темы