Generische Klassen (C++/CLI)

Eine generische Klasse wird mit dem folgenden Format deklariert:

[attributes]
generic <class-key type-parameter-identifier(s)>
[constraint-clauses]
[accessibility-modifiers] ref class identifier  [modifiers]
[: base-list] 
{
class-body
} [declarators] [;]

Hinweise

In der obigen Syntax werden die folgenden Begriffe verwendet:

  • attributes (optional)
    Zusätzliche deklarative Informationen.Weitere Informationen zu Attributen und Attributklassen finden Sie unter Attributes.

  • Klassenschlüssel
    Entweder class oder typename

  • TYPE-PARAMETER-Bezeichner,
    Eine durch Trennzeichen getrennte Liste mit Bezeichnern, die die Namen der Typparameter angeben.

  • Klauseln CONSTRAINT
    Eine Liste (nicht durch Trennzeichen getrennt) von WHERE-Klauseln, die die Einschränkungen für den Typparameter angeben.Entlädt die Form:

    whereTYPE-PARAMETER-Bezeichner:CONSTRAINT-Liste...

  • CONSTRAINT-Liste
    Klasse oder Schnittstelle,[ ...]

  • Modifizierer Barrierefreiheit
    Zugriffsmodifizierer für die generische Klasse.Für Windows-Runtimeist der einzige zulässige Modifizierer private.Während der Common Language Runtime sind die zulässigen Modifizierern und privatepublic.

  • identifier
    Der Name der generischen Klasse einen gültigen C++-Bezeichner.

  • Modifizierer (optional)
    Zulässige Modifizierer sind sealed und Zusammenfassung.

  • BasisListe
    Eine Liste, die die eine Basisklasse und alle implementierten Schnittstellen enthält, werden alle durch Kommas getrennt.

  • KlasseBODY
    Der Text der Klasse, Felder, Memberfunktionen enthalten usw.

  • Deklaratoren
    Deklarationen von Variablen aus diesem Typ.Beispiel:^Bezeichner[, …]

Sie können generische Klassen wie diese deklarieren (Beachten Sie, dass das Schlüsselwort anstelle Klasse möglicherweise Typnameverwendet wird).In diesem Beispiel werdenItemType, KeyType und ValueType unbekannte Typen, die an der Stelle in dem der Typ angegeben werden.HashTable<int, int> ist ein konstruierter Typ generische Typ HashTable<KeyType, ValueType>.Einige andere konstruierten Typen können aus einem einzelnen generischen Typs erstellt werden.Die konstruierten Typen, die von generischen Klassen erstellt werden, werden wie jeder andere ref-Klassentyp behandelt.

// 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>();
}

Werttypen werden (entweder integrierte Datentypen wie int oder doubleBenutzerdefinierte Werttypen und Referenztypen) oder als generisches Typargument verwendet werden.Die Syntax innerhalb der allgemeinen Definition ist unabhängig davon gleich.Syntaktisch wird der unbekannten Typ behandelt, als wäre er ein Verweistyp ist.Allerdings ist die Laufzeit möglich zu bestimmen, dass bei der tatsächlich verwendete Typ ein Werttyp ist und die entsprechenden generierten Code für direkten Zugriff auf den Member zu ersetzen.Die Werttypen, die als generische Typargumente verwendet werden, sind nicht geschachtelt. Dies erleiden keine Leistungseinbuße, die mit Boxing zugeordnet ist.Die Syntax, die innerhalb des Texts aus den generischen Typ verwendet wird, sollte T^- >„und“ anstelle von „.“ sein.Jeder Verwendung für ref new, gcnew (Komponentenerweiterungen für C++) den Typparameter wird von der Laufzeit geeignet als die einfache Erstellung eines Werttyps interpretiert, wenn das Typargument ein Werttyp ist.

Sie können eine generische Klasse mit auf den Einschränkungen für generische Typparameter (C++/CLI) Typen auch deklarieren, die für den Typparameter verwendet werden können.Im Folgenden Beispiel muss jeder Typ, der für ItemType verwendet wird, die IItem Schnittstelle implementieren.Versuchend, z. B. das intzu verwenden IItemnicht implementiert, wird ein Kompilierungsfehler erzeugen, da das Typargument der Einschränkung nicht erfüllt wird.

// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};

Generische Klassen im gleichen Namespace können nicht überladen werden, indem nur die Anzahl und die Typen von Typparametern ändert.Wenn jedoch jede Klasse in einem anderen Namespace lebt, können sie überladen werden.Betrachten Sie beispielsweise die folgenden zwei Klassen, MyClass und MyClass<ItemType>, in den Namespaces A und B.Die beiden Klassen können in einem dritten Namespace C dann überladen werden:

// 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
      }
   };
}

Die Basisklasse und die Basisschnittstellen können keine Typparameter sein.Allerdings kann die Basisklasse den Typparameter als Argument an, wie in den folgenden Fällen aufgeführt:

// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};

generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};

Konstruktoren und Destruktoren werden einmal für jede Objektinstanz wie gewohnt ausgeführt (); statische Konstruktoren werden einmal für jeden konstruierten Typ ausgeführt.

Felder in generischen Klassen

In diesem Abschnitt wird die Verwendung einer Instanz und statischen Feldern in generischen Klassen.

skef48fy.collapse_all(de-de,VS.110).gifInstanz-Variablen

Instanzvariablen einer generischen Klasse können Typen und Variableninitialisierer haben, die vom Typparameter aller einschließenden Klasse enthalten.

Beispiel

Im Folgenden Beispiel werden drei verschiedenen Instanzen der generischen Klasse, MyClass <ItemType> erstellt, indem die entsprechenden Typargumente verwendet (int, Doubleund Zeichenfolge).

// 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);
   }
  

Das folgende Beispiel veranschaulicht die Verwendung der statischen Feldern und einen statischen Konstruktor innerhalb einer generischen Klasse.

// 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();
}
  
  
  
  

Das folgende Beispiel deklariert eine nicht generische Methode, ProtectDatainnerhalb einer generischen Klasse, MyClass<ItemType>.Die Methode verwendet den Klassentypparameter ItemType in der zugehörigen Signatur in einem offenen konstruierten Typ.

// 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) {}
};

Die nicht generische Methode ist insofern noch generisch, dass sie mit dem Typparameter der Klasse parametrisiert wird, hat jedoch keine zusätzlichen Typparameter.

Alle Typen Methoden in generischen Klassen können generisch sein, einschließlich statisches Instanz und virtuelle Methoden.

Im Folgenden Beispiel wird das Deklarieren und Verwenden von generischen Methoden innerhalb von generischen Klassen:

// 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);
}
  

Dieses Beispiel zeigt Deklarationen einer Instanzeigenschaft innerhalb einer generischen Klasse an.

// 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);
}
  

Im Folgenden Beispiel wird eine generische Klasse mit einem Ereignis an.

// 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);
}

Das folgende Beispiel deklariert eine generische Struktur, MyGenStructmit einem Feld, myFieldund weist Werte verschiedener Typen (int, Double, String^) auf dieses Feld festgelegt werden soll.

// 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);
}
  

Statische Variablen

Bei der Erstellung eines neuen generischen Typs werden neue Instanzen aller statischen Variablen erstellt und ein statischer Konstruktor für diesen Typ wird ausgeführt.

Statische Variablen können alle Typparameter des einschließenden Klasse verwenden.

Methoden in generischen Klassen

Methoden in generischen Klassen selbst generisch sein können. nicht generische Methoden werden implizit vom Klassentypparameter parametrisiert.

Die folgenden Sonderregelungen gelten für Methoden in der generischen Klassen:

  • Methoden in generischen Klassen können Typparameter als Parameter oder Rückgabetypen, lokale Variablen verwenden.

  • Methoden in generischen Klassen können Sie die geschlossenen konstruierten Typen als Parameter oder Rückgabetypen, oder lokale Variablen verwenden.

skef48fy.collapse_all(de-de,VS.110).gifNicht generische Methoden in generischen Klassen

Methoden in generischen Klassen, die keine zusätzlichen Typparameter haben, wird normalerweise als nicht generisch, obwohl sie implizit durch die einschließende generische Klasse parametrisiert werden.

Die Signatur einer nicht generischen Methode kann einen oder mehrere Typparameter entweder der einschließenden Klasse oder in einem offenen konstruierten Typ direkt enthalten.Beispiele:

void MyMethod(MyClass<ItemType> x) {}

Der Text dieser Methoden kann auch mithilfe dieser Typparameter.

Generische Methoden in generischen Klassen

Sie können generische Methoden in generischen und nicht generischen Klassen deklarieren.Beispiele:

Verwenden von geschachtelten Typen in generischen Klassen

Wie bei gewöhnlichen Klassen können Sie andere Typen innerhalb einer generischen Klasse deklarieren.Die Deklaration der geschachtelten Klasse implizit von der Typparameter der äußeren Klassendeklaration parametrisiert.Auf diese Weise wird eine andere geschachtelte Klasse für jeden erstellten äußeren Typ definiert.In der Deklaration.

// generic_classes_5.cpp
// compile with: /clr /c
generic <typename ItemType>
ref struct Outer {
   ref class Inner {};
};

Der Typ des äußeren <int> :: Nicht entspricht der Typ äußeren <Double> : Inner: Inner.

Wie bei generischen Methoden in generischen Klassen können zusätzliche Typparameter für den geschachtelten Typ definiert sind.Wenn Sie dieselben Typparameternamen in der inneren und äußeren Klasse verwenden, wird der innere Typparameter der äußere Typparameter aus.

// 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
   };
};

Da es keine Möglichkeit gibt, den äußeren Typparameter zugreifen, erzeugt der Compiler eine Warnung in diesem Fall.

Wenn erstellte geschachtelte generische Typen benannt werden, wird der Typparameter für den äußeren Typ nicht in der Typparameterliste für den inneren Typ enthalten, obwohl der inneren Typ implizit vom äußeren Typparameter vom Typ parametrisiert wird.Im oben genannten Fall würde ein Name des konstruierten Typs außerhalb <int> : sein: Inneres <Zeichenfolge> .

Das folgende Beispiel veranschaulicht die Erstellung und Lesen einer verknüpften Liste mit geschachtelten Typen in generischen Klassen.

Eigenschaften, Ereignisse, Indexer und Operatoren in generischen Klassen

  • Eigenschaften, Ereignisse, Indexer und Operatoren können die Typparameter der einschließenden generischen Klasse als Rückgabewerte, Parameter und lokale Variablen, z. B. bei einer Klasse ist ein Typparameter ItemType :

    public ItemType MyProperty {}
    
  • Eigenschaften, Ereignisse, Indexer und Operatoren können selbst nicht parametrisiert sind.

Generische Strukturen

Die Regeln zum Deklarieren und Verwenden von generischen Strukturen sind identisch mit denen für generische Klassen, mit Ausnahme der Unterschiede, die im Visual C++-Sprachreferenz erwähnt werden.

Siehe auch

Weitere Ressourcen

Generika (Komponentenerweiterungen für C++)