ジェネリック クラス (C++/CLI)
ジェネリック クラスは次のフォームを使用して宣言する:
[attributes]
generic <class-key type-parameter-identifier(s)>
[constraint-clauses]
[accessibility-modifiers] ref class identifier [modifiers]
[: base-list]
{
class-body
} [declarators] [;]
解説
上の構文で、次の用語が使用されています:
attributes (省略可能)
追加の宣言情報。 属性と属性クラスの詳細については、属性を参照します。class-key
class または typename、
型パラメーターの名前を指定する識別子のコンマ区切りのリスト。constraint-clauses
型パラメーターに制約を指定する where 句のリスト (コンマ区切りいません)。 形式をとります。:where type-parameter-identifier : constraint-list ...
constraint-list
クラスまたはインターフェイスの, …]accessibility-modifiers
ジェネリック クラスのアクセシビリティ修飾子。 Windows ランタイムでは、唯一の許可された修飾子は privateです。 共通言語ランタイムでは、使用できる修飾子は private と publicです。identifier
ジェネリック クラスは、有効な C++ 識別子の名前。修飾子 (省略可能な)
使用できる修飾子は sealed と 抽象が含まれます。base-list
1 種類の基本クラス、実装されたインターフェイスを含む完全にコンマで区切られたリスト。class-body
フィールド、メンバー関数などを含むクラス本体宣言子
この変数の宣言型。 例:^id [, …]
キーワード class が typenameの代わりに使用できること) のようなジェネリック クラス (メモを宣言できます。 この例では、ItemType、KeyType と ValueType はポイントで型指定された不明な型です。 HashTable<int, int> は ジェネリック型 HashTable<KeyType, ValueType>の構築された型です。 複数の種類の構築された型は単一のジェネリック型から作成できます。 ジェネリック クラスから構築構築された型は他の ref クラス型と同様に扱われます。
// generic_classes_1.cpp
// compile with: /clr
using namespace System;
generic <typename ItemType>
ref struct Stack {
// ItemType may be used as a type here
void Add(ItemType item) {}
};
generic <typename KeyType, typename ValueType>
ref class HashTable {};
// The keyword class may be used instead of typename:
generic <class ListItem>
ref class List {};
int main() {
HashTable<int, Decimal>^ g1 = gcnew HashTable<int, Decimal>();
}
値型 (組み込み型、または int または doubleなどのユーザー定義の値型および参照型) は、どちらも、ジェネリック型引数として使用されることがあります。 一般的な定義内の構文でも同じです。 構文上、未知の型が参照型であるかのように扱われます。 ただし、ランタイムは実際に使用される型が値型の場合、そのメンバーに直接アクセスの適切な生成されたコードを代わりにできます。 ジェネリック型引数として使用される値型は囲まれないため、ボックス化と関連付けられているパフォーマンスの低下に苦しみません。 ジェネリックの本体で使用する構文は ###.の代わりに T^ と '->' 必要があります。 型引数が値型の場合、型パラメーターの ref new、gcnew (C++ コンポーネント拡張) の使用で値型の簡単な作成としてランタイムによって適切に処理されます。
また、型パラメーターとして使用できる型の ジェネリック型パラメーターの制約 (C++/CLI) のジェネリック クラスを宣言できます。 次の例で ItemType に使用する型が IItem インターフェイスを実装しなければなりません。 intを操作するには、まず、IItemを実装しない、型引数が制約を満たさないため、コンパイル エラーになります。
// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};
同じ名前空間のクラスはジェネリック型パラメーターの数と種類を変更するだけでオーバーロードすることはできません。 ただし、各クラスが異なる名前空間に住んでいる場合、オーバーロードすることができます。 たとえば、次の 2 種類のクラス、MyClass と名前空間 A と Bの MyClass<ItemType>を考えてみます。 2 個のクラスは、3 番目の名前空間 C でオーバーロードすることがあります:
// generic_classes_3.cpp
// compile with: /clr /c
namespace A {
ref class MyClass {};
}
namespace B {
generic <typename ItemType>
ref class MyClass2 { };
}
namespace C {
using namespace A;
using namespace B;
ref class Test {
static void F() {
MyClass^ m1 = gcnew MyClass(); // OK
MyClass2<int>^ m2 = gcnew MyClass2<int>(); // OK
}
};
}
基本クラスと基本インターフェイスは型パラメーターにすることはできません。 ただし、基本クラスは次のようなケースとして、引数として型パラメーターを含めることができますが、T:
// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};
generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};
コンストラクターとデストラクターは、オブジェクト インスタンスにつき一度実行されます (通常どおり) ; 静的コンストラクターは構築された型につき一度だけ実行されます。
ジェネリック クラスのフィールド
このセクションでは、ジェネリック クラスのインスタンスも静的フィールドを使用する方法を示します。
インスタンス変数
ジェネリック クラスのインスタンス変数は、外側のクラスの型パラメーターを含む変数初期化子、および型を持つことができます。
使用例
次の例では、ジェネリック クラス、MyClassItemType<>の 3 種類のインスタンスは、適切な型の引数 (int、doubleと 文字列) によって作成されます。
// generics_instance_fields1.cpp
// compile with: /clr
// Instance fields on generic classes
using namespace System;
generic <typename ItemType>
ref class MyClass {
// Field of the type ItemType:
public :
ItemType field1;
// Constructor using a parameter of the type ItemType:
MyClass(ItemType p) {
field1 = p;
}
};
int main() {
// Instantiate an instance with an integer field:
MyClass<int>^ myObj1 = gcnew MyClass<int>(123);
Console::WriteLine("Integer field = {0}", myObj1->field1);
// Instantiate an instance with a double field:
MyClass<double>^ myObj2 = gcnew MyClass<double>(1.23);
Console::WriteLine("Double field = {0}", myObj2->field1);
// Instantiate an instance with a String field:
MyClass<String^>^ myObj3 = gcnew MyClass<String^>("ABC");
Console::WriteLine("String field = {0}", myObj3->field1);
}
次の例は、ジェネリック クラス内の静的フィールドおよび静的コンストラクターを使用します。
// generics_static2.cpp
// compile with: /clr
using namespace System;
interface class ILog {
void Write(String^ s);
};
ref class DateTimeLog : ILog {
public:
virtual void Write(String^ s) {
Console::WriteLine( "{0}\t{1}", DateTime::Now, s);
}
};
ref class PlainLog : ILog {
public:
virtual void Write(String^ s) { Console::WriteLine(s); }
};
generic <typename LogType>
where LogType : ILog
ref class G {
static LogType s_log;
public:
G(){}
void SetLog(LogType log) { s_log = log; }
void F() { s_log->Write("Test1"); }
static G() { Console::WriteLine("Static constructor called."); }
};
int main() {
G<PlainLog^>^ g1 = gcnew G<PlainLog^>();
g1->SetLog(gcnew PlainLog());
g1->F();
G<DateTimeLog^>^ g2 = gcnew G<DateTimeLog^>();
g2->SetLog(gcnew DateTimeLog());
// prints date
// g2->F();
}
次の例は、非ジェネリックのメソッド、ProtectDataを、ジェネリック クラス内で、MyClass<ItemType>宣言します。 メソッドはオープン構築型のシグネチャにクラス型パラメーター ItemType を使用します。
// generics_non_generic_methods1.cpp
// compile with: /clr
// Non-generic methods within a generic class.
using namespace System;
generic <typename ItemType>
ref class MyClass {
public:
String^ name;
ItemType data;
MyClass(ItemType x) {
data = x;
}
// Non-generic method using the type parameter:
virtual void ProtectData(MyClass<ItemType>^ x) {
data = x->data;
}
};
// ItemType defined as String^
ref class MyMainClass: MyClass<String^> {
public:
// Passing "123.00" to the constructor:
MyMainClass(): MyClass<String^>("123.00") {
name = "Jeff Smith";
}
virtual void ProtectData(MyClass<String^>^ x) override {
x->data = String::Format("${0}**", x->data);
}
static void Main() {
MyMainClass^ x1 = gcnew MyMainClass();
x1->ProtectData(x1);
Console::WriteLine("Name: {0}", x1->name);
Console::WriteLine("Amount: {0}", x1->data);
}
};
int main() {
MyMainClass::Main();
}
// generics_method2.cpp
// compile with: /clr /c
generic <typename Type1>
ref class G {
public:
// Generic method having a type parameter
// from the class, Type1, and its own type
// parameter, Type2
generic <typename Type2>
void Method1(Type1 t1, Type2 t2) { F(t1, t2); }
// Non-generic method:
// Can use the class type param, Type1, but not Type2.
void Method2(Type1 t1) { F(t1, t1); }
void F(Object^ o1, Object^ o2) {}
};
非ジェネリックのメソッドは、クラスの型パラメーターによってパラメーター化されるが、追加の型パラメーターがないという意味でも一般的です。
ジェネリック クラスのメソッドのすべての型は、静的メソッドが、ジェネリック インスタンスで、仮想メソッドです。
次の例では、ジェネリック クラス内のジェネリック メソッドの宣言と使用方法を示しています。:
// generics_generic_method2.cpp
// compile with: /clr
using namespace System;
generic <class ItemType>
ref class MyClass {
public:
// Declare a generic method member.
generic <class Type1>
String^ MyMethod(ItemType item, Type1 t) {
return String::Concat(item->ToString(), t->ToString());
}
};
int main() {
// Create instances using different types.
MyClass<int>^ myObj1 = gcnew MyClass<int>();
MyClass<String^>^ myObj2 = gcnew MyClass<String^>();
MyClass<String^>^ myObj3 = gcnew MyClass<String^>();
// Calling MyMethod using two integers.
Console::WriteLine("MyMethod returned: {0}",
myObj1->MyMethod<int>(1, 2));
// Calling MyMethod using an integer and a string.
Console::WriteLine("MyMethod returned: {0}",
myObj2->MyMethod<int>("Hello #", 1));
// Calling MyMethod using two strings.
Console::WriteLine("MyMethod returned: {0}",
myObj3->MyMethod<String^>("Hello ", "World!"));
// generic methods can be called without specifying type arguments
myObj1->MyMethod<int>(1, 2);
myObj2->MyMethod<int>("Hello #", 1);
myObj3->MyMethod<String^>("Hello ", "World!");
}
// generics_linked_list.cpp
// compile with: /clr
using namespace System;
generic <class ItemType>
ref class LinkedList {
// The node class:
public:
ref class Node {
// The link field:
public:
Node^ next;
// The data field:
ItemType item;
} ^first, ^current;
};
ref class ListBuilder {
public:
void BuildIt(LinkedList<double>^ list) {
/* Build the list */
double m[5] = {0.1, 0.2, 0.3, 0.4, 0.5};
Console::WriteLine("Building the list:");
for (int n=0; n<=4; n++) {
// Create a new node:
list->current = gcnew LinkedList<double>::Node();
// Assign a value to the data field:
list->current->item = m[n];
// Set the link field "next" to be the same as
// the "first" field:
list->current->next = list->first;
// Redirect "first" to the new node:
list->first = list->current;
// Display node's data as it builds:
Console::WriteLine(list->current->item);
}
}
void ReadIt(LinkedList<double>^ list) {
// Read the list
// Make "first" the "current" link field:
list->current = list->first;
Console::WriteLine("Reading nodes:");
// Read nodes until current == null:
while (list->current != nullptr) {
// Display the node's data field:
Console::WriteLine(list->current->item);
// Move to the next node:
list->current = list->current->next;
}
}
};
int main() {
// Create a list:
LinkedList<double>^ aList = gcnew LinkedList<double>();
// Initialize first node:
aList->first = nullptr;
// Instantiate the class, build, and read the list:
ListBuilder^ myListBuilder = gcnew ListBuilder();
myListBuilder->BuildIt(aList);
myListBuilder->ReadIt(aList);
}
この例では、ジェネリック クラス内のインスタンスのプロパティ宣言を示しています。
// generics_generic_properties1.cpp
// compile with: /clr
using namespace System;
generic <typename ItemType>
ref class MyClass {
private:
property ItemType myField;
public:
property ItemType MyProperty {
ItemType get() {
return myField;
}
void set(ItemType value) {
myField = value;
}
}
};
int main() {
MyClass<String^>^ c = gcnew MyClass<String^>();
MyClass<int>^ c1 = gcnew MyClass<int>();
c->MyProperty = "John";
c1->MyProperty = 234;
Console::Write("{0}, {1}", c->MyProperty, c1->MyProperty);
}
次の例は、イベントのジェネリック クラスを示しています。
// generics_generic_with_event.cpp
// compile with: /clr
// Declare a generic class with an event and
// invoke events.
using namespace System;
// declare delegates
generic <typename ItemType>
delegate void ClickEventHandler(ItemType);
// generic class that defines events
generic <typename ItemType>
ref class EventSource {
public:
// declare the event OnClick
event ClickEventHandler<ItemType>^ OnClick;
void FireEvents(ItemType item) {
// raises events
OnClick(item);
}
};
// generic class that defines methods that will called when
// event occurs
generic <typename ItemType>
ref class EventReceiver {
public:
void OnMyClick(ItemType item) {
Console::WriteLine("OnClick: {0}", item);
}
};
int main() {
EventSource<String^>^ MyEventSourceString =
gcnew EventSource<String^>();
EventSource<int>^ MyEventSourceInt = gcnew EventSource<int>();
EventReceiver<String^>^ MyEventReceiverString =
gcnew EventReceiver<String^>();
EventReceiver<int>^ MyEventReceiverInt = gcnew EventReceiver<int>();
// hook handler to event
MyEventSourceString->OnClick += gcnew ClickEventHandler<String^>(
MyEventReceiverString, &EventReceiver<String^>::OnMyClick);
MyEventSourceInt->OnClick += gcnew ClickEventHandler<int>(
MyEventReceiverInt, &EventReceiver<int>::OnMyClick);
// invoke events
MyEventSourceString->FireEvents("Hello");
MyEventSourceInt->FireEvents(112);
// unhook handler to event
MyEventSourceString->OnClick -= gcnew ClickEventHandler<String^>(
MyEventReceiverString, &EventReceiver<String^>::OnMyClick);
MyEventSourceInt->OnClick -= gcnew ClickEventHandler<int>(
MyEventReceiverInt, &EventReceiver<int>::OnMyClick);
}
次の例では、一般的な構造体、MyGenStructを、1 フィールドと、myField宣言し、このフィールドに異なる型 (int、double、String^) の値を割り当てます。
// generics_generic_struct1.cpp
// compile with: /clr
using namespace System;
generic <typename ItemType>
ref struct MyGenStruct {
public:
ItemType myField;
ItemType AssignValue(ItemType item) {
myField = item;
return myField;
}
};
int main() {
int myInt = 123;
MyGenStruct<int>^ myIntObj = gcnew MyGenStruct<int>();
myIntObj->AssignValue(myInt);
Console::WriteLine("The field is assigned the integer value: {0}",
myIntObj->myField);
double myDouble = 0.123;
MyGenStruct<double>^ myDoubleObj = gcnew MyGenStruct<double>();
myDoubleObj->AssignValue(myDouble);
Console::WriteLine("The field is assigned the double value: {0}",
myDoubleObj->myField);
String^ myString = "Hello Generics!";
MyGenStruct<String^>^ myStringObj = gcnew MyGenStruct<String^>();
myStringObj->AssignValue(myString);
Console::WriteLine("The field is assigned the string: {0}",
myStringObj->myField);
}
静的変数
新しいジェネリック型の作成では、静的変数の新しいインスタンスが作成され、その型の静的コンストラクターが実行されます。
静的変数は、外側のクラスの型パラメーターを使用できます。
ジェネリック クラスのメソッド
ジェネリック クラスのメソッドは、ジェネリックにすることができます; 非ジェネリック クラスのメソッドは、型パラメーターによって暗黙的なパラメーター化されたです。
次の特別な規則がジェネリック クラス内のメソッドに適用する:
ジェネリック クラスのメソッドは、パラメーターと戻り値の型、またはローカル変数として型パラメーターを使用できます。
ジェネリック クラスのメソッドは、パラメーターと戻り値の型、またはローカル変数として開くか、クローズ構築型を使用できます。
ジェネリック クラスの非ジェネリック メソッド
追加の型パラメーターは、ジェネリック クラスのメソッドは、通常、非ジェネリックと外側のジェネリック クラスによって暗黙的なパラメーター化された実行と呼びます。
非ジェネリックのメソッドのシグネチャまたはオープン構築型の一つ以上の型パラメーターを外側のクラスの、直接含めることができます。 たとえば、次のようになります。
void MyMethod(MyClass<ItemType> x) {}
このようなメソッドの本体は、これらの型パラメーターを使用できます。
ジェネリック クラスとジェネリック メソッド
ジェネリックと非ジェネリック クラスの両方のジェネリック メソッドを宣言できます。 たとえば、次のようになります。
ジェネリック クラスの入れ子にされた型を使用します。
通常のクラスと同様に、ジェネリック クラス内の他の型を宣言できます。 入れ子になったクラスの宣言は外部クラスの宣言の型パラメーターによって暗黙的なパラメーター化されたです。 したがって、個々の入れ子になったクラスは、それぞれの構築された外部の型に対して定義されます。 たとえば、宣言、
// generic_classes_5.cpp
// compile with: /clr /c
generic <typename ItemType>
ref struct Outer {
ref class Inner {};
};
型<>Outerint::Inner は型<>Outerdouble::Inner と同じではありません。
ジェネリック クラスとジェネリック メソッドの場合と同様に、追加の型パラメーターは、入れ子にされた型に対して定義できます。 内部および外部クラスに同じ型パラメーター名を使用する場合は、内部の型パラメーターは外側の型パラメーターを非表示にします。
// generic_classes_6.cpp
// compile with: /clr /c
generic <typename ItemType>
ref class Outer {
ItemType outer_item; // refers to outer ItemType
generic <typename ItemType>
ref class Inner {
ItemType inner_item; // refers to Inner ItemType
};
};
外側の型パラメーターを示す方法がないため、コンパイラはこの場合は警告が生成されます。
構築されたジェネリック型に名前を付けると、外側の型の型パラメーターは、内部の型の型パラメーター リスト内の型が、外側の型の型パラメーターによって暗黙的なパラメーター化された実行が、含まれません。 このような場合、構築された型の名前は Outerint::Innerstring<>です<>。
次の例では、ジェネリック クラスの入れ子にされた型を使用してビルドと読み取りをリンク リストに示します。
ジェネリック クラスのプロパティ、イベント、インデクサー、および演算子
ItemType がクラスの型パラメーターの場合、プロパティ、インデクサー、イベント、演算子、戻り値、パラメーターとして外側のジェネリック クラスの型パラメーターを、またはローカル変数など、使用する:
public ItemType MyProperty {}
プロパティ、インデクサー、イベント、演算子自体をパラメーター化することはできません。
一般的な構造体
一般的な構造体を宣言し、Visual C++ 言語のリファレンスで説明した違いを除き、ジェネリック クラスの定義に使用するための規則は同じです。