ジェネリック クラス (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 か typenametype-parameter-identifier(s),
型パラメーターの名前を指定する ID のコンマ区切りのリスト。制約句
型パラメーターに制約を指定する where の句のリスト (カンマ区切られていない)。:とおりです。where型パラメーター ID:制約リスト...
制約リスト
クラスまたはインターフェイス,[ …]アクセシビリティ修飾子
ジェネリック クラスのアクセシビリティの修飾子。Windows ランタイムでは、唯一許容される修飾子は privateです。共通言語ランタイムの場合は、許可される修飾子は private と publicです。identifier
ジェネリック クラス、有効な C++ の識別名。修飾子 (省略可能)
使用できる修飾子は sealed と 抽象が含まれます。ベース リスト
1 種類の基本クラス、実装されたインターフェイスを含む完全にカンマで区切ったリスト。クラス本体
フィールド、メンバー関数、先祖などを含むクラスの本体宣言子
この変数の宣言型。例:^のID, […]
キーワード クラス が 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 番目のクラスは、名前空間の 15 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
}
};
}
基本クラスや基本インターフェイスは、型パラメーターにすることはできません。ただし、基本クラスは次のような場合として引数として型パラメーターを含めることもできます:
// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};
generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};
(通常は)コンストラクターとデストラクターは、オブジェクト インスタンスごとに一度だけ実行されます; 静的コンストラクターは、構築型につき回実行されます。
ジェネリック クラスのフィールド
このセクションでは、ジェネリック クラスのインスタンスと静的フィールドの使用方法を示します。
インスタンス変数
ジェネリック クラスのインスタンス変数は、外側のクラスの型パラメーターを含む変数初期化子と型を持つことができます。
使用例
次の例では、ジェネリック クラス、 MyClass <ItemType> の 3 種類の異なるインスタンスは、適切な型の引数 (int、 倍精度浮動小数点型と 文字列)を使用して、作成されます。
// 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、 倍精度浮動小数点型、 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 {};
};
外側の型 <int> :: 内部同じ型は、外側の <倍精度浮動小数点型> とではありません:: 内部。
ジェネリック クラスのジェネリック メソッドと同様に、追加の型パラメーターは入れ子にされた型に対して定義することができます。内部および外部クラスで同じ型パラメーターの名前を使用すると、内側の型パラメーターは外側の型パラメーターを非表示にします。
// 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
};
};
外側の型パラメーターを示す方法がないため、コンパイラはこの場合は警告を生成します。
構築された入れ子になったジェネリック型を指定すると、外側の型の型パラメーターは内部の型の型パラメーター リストに内部の型が外側の型の型パラメーターによって暗黙的にパラメーター化されていても、含まれません。このような場合、構築型の名前は、外側の <int> です:: 内部の <文字列> 。
次の例では、ジェネリック クラスの入れ子にされた型を使用してビルドと読み取りをリンク リスト示します。
ジェネリック クラスのプロパティ、イベント、および演算子インデクサ
ItemType がクラスの型パラメーターの場合、プロパティ、イベント、インデクサおよび演算子は戻り値、パラメーターとして外側のジェネリック クラスの型パラメーターを、ローカル変数、のなどのを使用する:
public ItemType MyProperty {}
プロパティ、イベント、インデクサおよび演算子自体は、をパラメーター化できません。
ジェネリック構造体
ジェネリック構造体を宣言し、 Visual C++ の言語リファレンスで説明した違いを除き、ジェネリック クラスの場合と使用するための規則は同じです。