泛型類別 (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或typename型別參數的識別碼(s),
指定的型別參數名稱的識別項的逗號分隔清單。限制式的子句
一份 (未以逗號分隔) , 子句指定的型別參數條件約束。採用下列表單:where 型別參數的識別碼 : 限制式-清單 ...
限制式的清單
類別-or-介面, ...協助工具的修飾詞
泛型類別的存取範圍修飾詞。對於Windows 執行階段,只允許修飾詞是private。Common Language Runtime時,允許的修飾詞是private和public。identifier
泛型類別,任何有效C++識別碼名稱。修飾詞 (可省略)
允許修飾詞包括sealed和抽象。基底清單
清單包含一個基底類別和任何實作介面,以逗號分隔。類別的內文
主體類別,包含欄位、 成員函式等等。宣告子
這種類型的任何變數的宣告。For example: ^識別碼, ...]
您可以宣告這類的泛型類別 (請注意,關鍵字類別 可能會用來代替 型別名稱)。在這個範例中, 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,或使用者-定義實值型別) 和參考型別可以作為泛型型別引數。 在泛型定義中的語法都是相同的不管。在語法上,就好像是參考型別都會被視為無法辨識的型別。不過,執行階段就能夠判斷,實際使用的型別是一種實值型別和替代適當的直接存取成員產生的程式碼。實值型別做為泛型型別引數不盤,因此不會受boxing相關聯的效能負面影響。使用本文中的泛型語法應為 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 {};
在相同的命名空間中的泛型類別不可被多載只變更數目或型別參數的型別。不過,如果每個類別會存在於不同的命名空間中,它們可以被多載。比方說,請考慮下列的兩個類別, MyClass和MyClass<ItemType>,命名空間中A和B。這兩個類別可以再被多載中第三個命名空間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>,不同的執行個體 (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,有一個欄位, myField,並指派不同型別的值 (int, 雙, 字串 ^) 到此欄位。
// 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>:: 內部並不相同的型別外部 <double>:: 內部。
為具有泛型方法中泛型類別,其他的型別參數可以定義巢狀型別。如果您使用相同的型別參數名稱在內部和外部類別時,內部型別參數會隱藏外部型別參數。
// 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>:: 內部 <string>。
下列範例會示範建立和讀取使用巢狀型別,泛型類別中的連結的清單。
屬性、 事件、 索引子和運算子在泛型類別
屬性、 事件、 索引子和運算子可用在封閉式的泛型類別的型別參數為傳回值、 參數或區域ItemType是類別的型別參數:
public ItemType MyProperty {}
屬性、 事件、 索引子和運算子不能自行加以參數化。
泛用結構
宣告和使用泛型結構的規則是一樣的泛型類別,除了用於 Visual C++ 語言參考中所述的差異。