Ограничения параметров универсального типа (C++/CLI)

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

Ограничения являются необязательными; не указывая ограничение для параметра, эквивалентно использованию Object ограничения.

Синтаксис

where type-parameter : constraint-list

Параметры

type-parameter
Один из идентификаторов параметров типа для ограничения.

constraint-list
Список спецификаций ограничений, разделенных запятыми. Список может включать интерфейсы, которые будут реализованы type-parameter.

Список также может содержать класс. Чтобы удовлетворить ограничение базового класса, аргумент типа должен быть тем же классом, что и ограничение или производный от ограничения. Укажитеref class, чтобы указать, что аргумент типа должен быть ссылочным типом, включая любой class, interfacedelegateили array тип. Укажите value class , чтобы указать, что аргумент типа должен быть типом значения. Допускается задание любого типа значения, кроме Nullable<T>.

Можно также указать gcnew() , что аргумент типа должен иметь открытый конструктор без параметров.

В качестве ограничения можно также указать универсальный параметр. Аргумент типа, предоставленный для типа, который вы ограничиваете, должен быть или производным от типа ограничения. Этот параметр называется ограничением голого типа.

Замечания

Предложение ограничения состоит из where параметра типа, двоеточия (:) и ограничения, указывающего характер ограничения для параметра типа. where — контекстно-зависимое ключевое слово. Дополнительные сведения см. в ключевое слово с учетом контекста. Разделение нескольких where предложений с пробелом.

Ограничения применяются к параметрам типа для задания ограничений на типы, которые могут использоваться в качестве аргументов для универсальных типов и методов.

Ограничения класса и интерфейса определяют, что типы аргументов должны быть указанного класса или унаследованы от него либо должны реализовывать указанный интерфейс.

Применение ограничений к универсальному типу или методу позволяет коду в этом типе или методе использовать известные возможности ограниченных типов. Например, можно объявить универсальный класс так, чтобы параметр типа реализовывал интерфейс IComparable<T>:

// generics_constraints_1.cpp
// compile with: /c /clr
using namespace System;
generic <typename T>
where T : IComparable<T>
ref class List {};

Это ограничение требует, чтобы аргумент типа, используемый для T, реализовывал IComparable<T> во время компиляции. Оно также позволяет вызывать методы интерфейса, например CompareTo. Для вызова методов интерфейса приведение типа в экземпляре параметра типа не требуется.

Статические методы в классе аргумента типа не могут вызываться с помощью параметра типа; их можно вызывать только с помощью фактического именованного типа.

Ограничение не может быть типом значения, включая встроенные типы, например int или double. Так как типы значений не могут иметь производные классы, только один класс когда-либо может удовлетворить ограничение. В этом случае универсальный шаблон можно переписать с заменой параметра типа определенным типом значения.

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

Для одного параметра типа можно определить несколько ограничений в списке, разделенном запятыми.

// generics_constraints_2.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;
generic <typename T>
where T : List<T>, IComparable<T>
ref class List {};

При наличии нескольких параметров типа для каждого из них следует использовать по одному предложению where. Например:

// generics_constraints_3.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;

generic <typename K, typename V>
   where K: IComparable<K>
   where V: IComparable<K>
ref class Dictionary {};

Используйте ограничения в коде в соответствии со следующими правилами:

  • Ограничения можно перечислять в любом порядке.

  • Ограничения также могут быть типами классов, например абстрактными базовыми классами. Однако ограничения не могут быть типами значений или sealed классами.

  • Ограничения не могут быть параметрами типа, но они могут включать параметры типа в открытый созданный тип. Например:

    // generics_constraints_4.cpp
    // compile with: /c /clr
    generic <typename T>
    ref class G1 {};
    
    generic <typename Type1, typename Type2>
    where Type1 : G1<Type2>   // OK, G1 takes one type parameter
    ref class G2{};
    

Примеры

В следующем примере демонстрируется использование ограничений для вызова экземплярных методов для параметров типа.

// generics_constraints_5.cpp
// compile with: /clr
using namespace System;

interface class IAge {
   int Age();
};

ref class MyClass {
public:
   generic <class ItemType> where ItemType : IAge
   bool isSenior(ItemType item) {
      // Because of the constraint,
      // the Age method can be called on ItemType.
      if (item->Age() >= 65)
         return true;
      else
         return false;
   }
};

ref class Senior : IAge {
public:
   virtual int Age() {
      return 70;
   }
};

ref class Adult: IAge {
public:
   virtual int Age() {
      return 30;
   }
};

int main() {
   MyClass^ ageGuess = gcnew MyClass();
   Adult^ parent = gcnew Adult();
   Senior^ grandfather = gcnew Senior();

   if (ageGuess->isSenior<Adult^>(parent))
      Console::WriteLine("\"parent\" is a senior");
   else
      Console::WriteLine("\"parent\" is not a senior");

   if (ageGuess->isSenior<Senior^>(grandfather))
      Console::WriteLine("\"grandfather\" is a senior");
   else
      Console::WriteLine("\"grandfather\" is not a senior");
}
"parent" is not a senior
"grandfather" is a senior

Если параметр универсального типа используется в качестве ограничения, он называется ограничением голого типа. Открытые ограничения типа применимы, когда функция-член со своим параметром типа должна ограничивать этот параметр параметром содержащего типа.

В приведенном ниже примере T — открытое ограничение типа в контексте метода Add.

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

// generics_constraints_6.cpp
// compile with: /clr /c
generic <class T>
ref struct List {
   generic <class U>
   where U : T
   void Add(List<U> items)  {}
};

generic <class A, class B, class C>
where A : C
ref struct SampleClass {};

См. также

Универсальные шаблоны