ジェネリック型パラメーターの制約 (C++/CLI)
ジェネリック型またはメソッドの宣言では、型パラメーターを "制約" で修飾できます。 制約は、型引数として使用される型が満たす必要がある要件です。 たとえば、型引数で特定のインターフェイスを実装するか特定のクラスから継承する必要があるという制約が可能です。
制約は省略可能です。パラメーターに制約を指定しないのは、 Object 制約の使用と同じです。
構文
where
type-parameter
:
constraint-list
パラメーター
type-parameter
制約する型パラメーター識別子の 1 つ。
constraint-list
制約仕様のコンマ区切りのリスト。 リストには、 type-parameter
によって実装されるインターフェイスを含めることができます。
一覧には、クラスを含めることもできます。 基底クラスの制約を満たすには、型引数が制約と同じクラスであるか、制約から派生している必要があります。 型引数が参照型である必要があることを示す ref class
を指定します。これには、 class
、 interface
、 delegate
、または 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
などの組み込み型を含め、制約を値型にすることはできません。 値型は派生クラスを持つことができないため、制約を満たすことができるクラスは 1 つだけです。 その場合は、特定の値の型に置き換えられた型パラメーターを使用して、ジェネリックを書き換えることができます。
不明な型でメソッドやインターフェイスがサポートされていることが制約で示唆されない限り、コンパイラで不明な型のメソッドやその他の機能は使用できないため、制約が必要な場合があります。
同じ型パラメーターに対して、コンマ区切りの一覧で複数の制約を指定できます。
// 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 {};
複数の型パラメーターがある場合は、型パラメーターごとに 1 つの 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 から派生することを除き、生の型制約に関して想定できることがないためです。 2 つの型パラメーター間に継承関係を適用するシナリオには、ジェネリック クラスに対する生の型制約を使用してください。
// 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 {};