Klasy rodzajowe (C + +/ CLI)
Klasą rodzajową jest zadeklarowana za pomocą następującej formularz:
[attributes]
generic <class-key type-parameter-identifier(s)>
[constraint-clauses]
[accessibility-modifiers] ref class identifier [modifiers]
[: base-list]
{
class-body
} [declarators] [;]
Uwagi
W składni powyżej używane są następujące warunki:
attributes(opcjonalnie)
Dodatkowe informacje deklaracyjne.Aby uzyskać więcej informacji dotyczących atrybutów i klas atrybut Zobacz atrybuty.klucz klasy
Albo class lubtypenameTypparametr-identyfikator(s),
Rozdzielana przecinkami lista identyfikatorów, określając nazwy parametrów typu.ograniczenie-klauzul
Lista (nie rozdzielanych przecinkami) z gdzie klauzule określające ograniczenia parametrów typu.Pakowany jest w formularz:where Typparametr-identyfikator : ograniczenie-lista ...
ograniczenie-lista
class-or-interfejs, ...ułatwienia dostępu-modyfikatorów
Modyfikatorów ułatwień dostępu dla klasą rodzajową.Dla Środowisko wykonawcze systemu Windows, tylko dozwolone jest modyfikator private.aparat plików wykonywalnych języka wspólnego, są dozwolone modyfikatorów private i public.identyfikator
Nazwa klasy rodzajowe, dowolny prawidłowy C++ identyfikator.Modyfikatory (opcjonalnie)
Dozwolone modyfikatorów obejmują sealed i abstrakcyjna.Lista podstawowego
Lista zawierająca jeden klasy podstawowej i wszelkich implementowane interfejsy wszystkich oddzielone przecinkami.Treść klasy
Treść klasy, zawierające pola funkcji elementów członkowskich, itp.declarators
Deklaracje dowolne zmienne tego typu.For example: ^identyfikator, ...]
Można zadeklarować klas rodzajowych, takich jak te (należy zauważyć, że słowo kluczowe klasy może być używany zamiast typename).W tym przykładzie ItemType, KeyType i ValueType są nieznane typy, które są określone w punkcie, gdzie typ.HashTable<int, int>jest skonstruowany typu typ ogólny HashTable<KeyType, ValueType>.Liczba różnych typów konstruowanej mogą być skonstruowane z pojedynczego typ ogólny.Typy konstruowanej zbudowane z klas rodzajowych są traktowane jak innego typu klasy 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>();
}
Wartości obu typów (albo wbudowanych typów, takich jak int lub double, lub na użytkownik-określonych typów wartości) i typy odwołań mogą być używane jakoargument typ ogólny. Składnia definicji rodzajowy jest taki sam bez względu na to.Syntaktycznie nieznany typ jest traktowany tak, jakby była typ referencyjny.Jednakże środowiska wykonawczego jest w stanie ustalenie, że jeżeli faktycznie użyty typ jest typ wartości i zastąpić odpowiednie wygenerowany kod dla dostęp bezpośredni do elementów członkowskich.Używane jako argumenty typ ogólny typów wartości nie są ramkach i tak nie ponieść karę wydajność związane z konwersja boxing.Składnia używana w ramach organu rodzajową powinny być T ^ i "->"zamiast".'.Każde użycie ref new, gcnew (C++ Component Extensions) dla typu parametr będzie odpowiednio interpretowany przez aparat plików wykonywalnych jako proste tworzenie typ wartości , jeśli typ argument jest typ wartości.
Można również zadeklarować klasą rodzajową z Ograniczenia rodzajowy wpisz parametry (C + +/ CLI) na typy, które mogą być używane dla typu parametr.W poniższym przykładzie dowolnego typu używane do ItemType musi implementować IItem interfejs. Podjęto próbę użycia int, na przykład, który nie implementuje IItem, przyniosłoby kompilować-czas błąd, ponieważ typ argument nie spełnia ograniczenie.
// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};
Klasy rodzajowe w tym samym przestrzeń nazw nie można obciążać tylko zmieniając liczbę lub typy parametrów typu.Jednakże jeśli każda klasa mieszka w innej przestrzeń nazw, one może być nadmiernie obciążony.Na przykład, rozważmy następujące dwie klasy MyClass i MyClass<ItemType>, w obszarach nazw A i B.Następnie może być nadmiernie obciążony dwóch klas w trzecim przestrzeń nazw 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
}
};
}
Klasa podstawowa i interfejsach podstawowych nie może być parametrów typu.Jednakże klasa podstawowa może dotyczyć typu parametr jako argument, jak w przypadku następujących:
// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};
generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};
Konstruktory i destruktory są wykonywane raz dla każdej instancji obiekt (w zwykły sposób); Konstruktory statyczny są uruchamiane raz dla każdego typu konstruowanej.
Pola w klas rodzajowych
W tej sekcji demonstruje użycie instancji i statyczny pól w klas rodzajowych.
Zmienne instancji
Zmienne instancji klasy rodzajowe mogą mieć typy i zmienna inicjatorów, które zawierają wszystkie parametry typu z otaczającym klasy.
Przykład
W poniższym przykładzie trzech różnych wystąpieniach klasą rodzajową MojaKlasa <ItemType>, są tworzone przy użyciu argumentów odpowiedniego typu (int, podwójne, i string).
// 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);
}
Następujący przykład demonstruje użycie statyczny pola ikonstruktor statycznyw ramach klasą rodzajową.
// 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();
}
Poniższy przykład oświadcza, innego niż-metoda ogólna ProtectData, wewnątrz klasy rodzajowej, MyClass<ItemType>.metoda używa parametr typu klasyItemType w jego podpis w polu otwarty zbudowane typu.
// 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) {}
};
Non-metoda ogólna jest nadal rodzajowe w tym sensie, że jest sparametryzowana przez klasę typu parametr, ale go nie ma typu dodatkowych parametrów.
Wszystkie rodzaje metod klas rodzajowych może być rodzajowy, włączając statyczny, wystąpienia i metodach wirtualnych.
Poniższy przykład demonstruje deklarowanie i przy użyciu metody rodzajowe wewnątrz klas rodzajowych:
// 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);
}
W tym przykładzie przedstawiono deklaracje instancji właściwość w ramach klasą rodzajową.
// 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);
}
W następnym przykładzie pokazano klasą rodzajową do zdarzenie.
// 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);
}
Poniższy przykład deklaruje rodzajowy struct, MyGenStruct, z jednego pole, myFieldi przypisuje wartości różnych typów (int, double, ciąg ^) do tego pole.
// 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);
}
Zmienne statyczne
Przy tworzeniu nowego typ ogólnytworzone są nowe wystąpienia jakichkolwiek zmiennych statyczny i wszelkie statyczny konstruktor dla tego typu jest wykonywany.
Zmienne statyczne można używać żadnych parametrów typu z klasy okalającego.
Metody rodzajowe klas
Metody rodzajowe klas może być rodzajowy same; metody rodzajowe nie będą niejawnie sparametryzowana przez parametrtypu klasy.
Następujące specjalne zasady stosuje się do metod wewnątrz klas rodzajowych:
Metody rodzajowe klas można używać parametrów typu jako parametry, zwracanych typów lub zmiennych lokalnych.
Metod klas rodzajowych można użyć, otwarty lub zamknięte typy skonstruowane jako parametry, zwracanych typów lub zmiennych lokalnych.
-Uniwersalne metod klas rodzajowych
Metody rodzajowe klas, które mają żadnych parametrów typu dodatkowe są zazwyczaj nazywane nierodzajową Chociaż one niejawnie są parametryzowane przez otaczający klasą rodzajową.
Podpis non -metoda ogólna może zawierać jeden lub więcej parametrów typ otaczający klasy, albo bezpośrednio lub w polu otwarty zbudowane typu.Na przykład:
void MyMethod(MyClass<ItemType> x) {}
Treść takich metod, można również użyć tych parametrów typu.
Metody rodzajowe w klas rodzajowych
Można zadeklarować metody rodzajowe rodzajowa i nierodzajową klas.Na przykład:
Za pomocą zagnieżdżone typy rodzajowe klas
Podobnie jak w zwykłych klasach, możesz zadeklarować innych typów wewnątrz klasą rodzajową.Deklaracja klasy zagnieżdżonych niejawnie jest parametryzowana przez parametry typu w deklaracji klasy zewnętrzne.W ten sposób różne klasy zagnieżdżonych jest zdefiniowany dla każdego konstruowanej typu zewnętrznego.Na przykład w deklaracji,
// generic_classes_5.cpp
// compile with: /clr /c
generic <typename ItemType>
ref struct Outer {
ref class Inner {};
};
Typ zewnętrzne <int>:: wewnętrzne nie jest taki sam, jak typ zewnętrzne <double>:: wewnętrzne.
Jako metody rodzajowe w klas rodzajowych parametrów typu dodatkowe może być określony dla typ zagnieżdżony.Jeśli używasz tej samej nazwy parametr typu klasy wewnętrzne i zewnętrzne wewnętrzny typ parametr spowoduje ukrycie zewnętrzne typu parametr.
// 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
};
};
Ponieważ sposobem odnoszą się do parametrtypu zewnętrznego, kompilator wygeneruje ostrzeżenie w tej sytuacji.
Typu parametr dla typu zewnętrznego konstruowanej zagnieżdżone typy rodzajowe są nazwane, jest nie uwzględnione na liście typ parametr typu wewnętrzny, mimo że wewnętrzny typ jest niejawnie sparametryzowana przez zewnętrzne typu parametr.W powyższym przypadku nazwa typu konstruowanej byłoby zewnętrzne <int>:: Inner <string>.
Poniższy przykład ilustruje tworzenie i odczytywanie połączonej listy przy użyciu zagnieżdżone typy rodzajowe klas.
Właściwości, zdarzenia, indeksatory i operatory klas rodzajowy
Właściwości, zdarzenia, indeksatory i operatorów, można używać parametrów typ otaczający klasą rodzajową jako wartości zwracane, parametry lub zmiennych lokalnych, jak np. przy ItemType jest to parametr typu klasy:
public ItemType MyProperty {}
Właściwości, zdarzenia, indeksatory i podmioty gospodarcze nie mogą same być parametryczne.
Rodzajowy strukturach
Zasady deklarowania i przy użyciu struktur rodzajowe są takie same, jak dla klas rodzajowych, z wyjątkiem dla różnic, odnotowane w w Skorowidzu języka Visual C++.