Partager via


Classes génériques (C++/CLI)

Une classe générique est déclarée avec la forme suivante :

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

Notes

dans la syntaxe ci-dessus, les termes suivants sont utilisés :

  • attributes (facultatif)
    les informations déclaratives supplémentaires.Pour plus d'informations sur les attributs et les classes d'attributs, consultez les attributs.

  • clé de classe
    class ou typename

  • type-paramètre-identificateur(s),
    Liste avec la virgule comme séparateur d'identificateurs spécifiant les noms de paramètres de type.

  • contrainte-clauses
    Une liste (non délimitée par des virgules) de clauses de where spécifiant les contraintes pour les paramètres de type.Prend la forme :

    wheretype-paramètre-identificateur:liste de contraintes...

  • liste de contraintes
    classe-ou-interface[, ]

  • Accessibility-modificateurs
    Modificateurs d'Accessibilité pour la classe générique.Pour Windows Runtime, le seul modificateur autorisé est private.Pour le common langage runtime, les modificateurs autorisés sont private et public.

  • identificateur
    Le nom de la classe générique, tout identificateur C++ valide.

  • modificateurs (facultatif)
    Les modificateurs autorisés incluent sealed et abstract.

  • base-liste
    Une liste qui contient l'une classe de base et les interfaces implémentées, tout en les séparant par des virgules.

  • classe-corps
    Le corps de la classe, contenant des champs, des fonctions membres, etc.

  • déclarateurs
    Déclarations de toutes variables de ce type.par exemple :identificateur d'^[, …]

Vous pouvez déclarer les classes génériques tels que ceux (notez que le mot clé class peut être utilisé au lieu de typename).Dans cet exemple,ItemType, KeyType et ValueType sont des types inconnus spécifiés au point où le type.HashTable<int, int> est un type construit du type générique HashTable<KeyType, ValueType>.Un certain nombre de types construits peuvent être construits d'un type générique unique.Les types construits construits les classes génériques sont traités comme tout autre type de classe de référence.

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

Les types valeur (les types intégrés tels qu' int ou double, ou des types valeur définis par l'utilisateur) et les types référence sont également utilisés comme argument de type générique.La syntaxe dans la définition générique est identique malgré tout.Du point de vue syntaxique, le type inconnu est traité comme s'il s'agissait d'un type référence.Toutefois, le runtime peut déterminer si le type réellement utilisé est un type valeur et en substituant le code généré approprié pour l'accès direct aux membres.Les types valeur utilisés comme arguments de type générique ne sont pas boxed et ne connaissent pas l'altération des performances associée à une conversion boxing.La syntaxe utilisée dans le corps du générique doit être T^ et « -> » au lieu de « . ».Toute utilisation de ref new, gcnew (extensions du composant C++) pour le paramètre de type est correctement interprétée par le runtime comme conception simple d'un type valeur si l'argument de type est un type valeur.

Vous pouvez également déclarer une classe générique avec Contraintes sur les paramètres de type générique (C++/CLI) des types qui peuvent être utilisés pour le paramètre de type.dans l'exemple suivant n'importe quel type utilisé pour ItemType doit implémenter l'interface d' IItem .Vous tentez d'utiliser int, par exemple, qui n'implémente pas IItem, produit une erreur de compilation parce que l'argument de type ne satisfait pas à la contrainte.

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

Les classes génériques dans le même espace de noms ne peuvent pas être surchargées en modifiant uniquement le nombre ou les types de paramètres de type.Toutefois, si chaque classe figure dans un espace de noms différent, ils peuvent être surchargés.par exemple, considérez les deux classes suivantes, MyClass et MyClass<ItemType>, dans les espaces de noms A et B.Les deux classes peuvent ensuite être surchargées dans un troisième espace de noms 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
      }
   };
}

les interfaces de classe de base et de base ne peuvent pas être des paramètres de type.Toutefois, la classe de base peut impliquer le paramètre de type comme argument, comme dans le cas suivant :

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

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

Les constructeurs et les destructeurs sont exécutés une fois pour chaque instance de l'objet (normalement) ; les constructeurs statiques sont exécutés une fois pour chaque type construit.

champs dans les classes génériques

Cette section illustre l'utilisation de champs d'instance et de statique dans les classes génériques.

skef48fy.collapse_all(fr-fr,VS.110).gifvariables d'instance

Les variables d'instance d'une classe générique peuvent avoir des types et des initialiseurs variables qui incluent tous les paramètres de type de la classe englobante.

Exemple

Dans l'exemple suivant, trois instances différentes de la classe générique, MyClass <ItemType> , sont créées à l'aide de les arguments de type appropriés (int, double, et 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);
   }
  

L'exemple suivant illustre l'utilisation des champs statiques et un constructeur statique dans une classe générique.

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

L'exemple suivant déclare une méthode non générique, ProtectData, dans une classe générique, MyClass<ItemType>.la méthode utilise le paramètre de type ItemType de classe dans sa signature dans un type construit ouvert.

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

La méthode non générique est encore générique dans le sens où elle est paramétrée par le paramètre de type de la classe, mais elle n'a pas de paramètres de type supplémentaire.

Tous les types de méthodes dans les classes génériques peuvent être génériques, et notamment la méthode statique, instance, et des méthodes virtuelles.

L'exemple suivant illustre la déclaration et l'utilisation des méthodes génériques dans les classes génériques :

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

cet exemple montre des déclarations d'une propriété d'instance dans une classe générique.

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

L'exemple suivant illustre une classe générique avec un événement.

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

L'exemple suivant déclare une structure générique, MyGenStruct, avec un champ, myField, et assigne des valeurs de types (int, double, String^) à ce champ.

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

variables statiques

Sur la création d'un nouveau type générique, de nouvelles instances de toutes les variables statiques sont créées et un constructeur statique pour ce type est exécuté.

les variables statiques peuvent utiliser tous les paramètres de type de la classe englobante.

méthodes dans les classes génériques

les méthodes dans les classes génériques peuvent être génériques elles-mêmes ; les méthodes non génériques sont implicitement paramétrables par le paramètre de type classe.

Les règles spécifiques suivantes s'appliquent aux méthodes dans les classes génériques :

  • Les méthodes dans les classes génériques peuvent utiliser des paramètres de type comme paramètres, des types de retour, ou variables locales.

  • Les méthodes dans les classes génériques peuvent utiliser des types construits ouverts ou fermés comme paramètres, des types de retour, ou variables locales.

skef48fy.collapse_all(fr-fr,VS.110).gifméthodes non génériques dans les classes génériques

Les méthodes dans les classes génériques qui n'ont pas de paramètres de type supplémentaire sont généralement mentionnées comme non générique bien qu'ils soient implicitement paramétrables par la classe générique englobante.

La signature d'une méthode non générique peut contenir un ou plusieurs paramètres de type de la classe englobante, directement ou dans un type construit ouvert.Par exemple :

void MyMethod(MyClass<ItemType> x) {}

le corps de telles méthodes peut également utiliser ces paramètres de type.

méthodes génériques dans les classes génériques

vous pouvez déclarer des méthodes génériques dans les classes génériques et non génériques.Par exemple :

À l'aide de imbriqué dans les classes génériques

Comme avec les classes normales, vous pouvez déclarer d'autres types dans une classe générique.la déclaration de classe imbriquée est implicitement paramétrable par les paramètres de type de la déclaration de classe externe.Ainsi, une classe imbriquée séparée est définie pour chaque type externe construit.par exemple, dans la déclaration,

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

le type <int> externe : : interne n'est pas le même que le type <double> externe : : interne.

Comme avec les méthodes génériques dans les classes génériques, les paramètres de type supplémentaires peuvent être définis pour le type imbriqué.Si vous utilisez les mêmes noms de paramètres de type dans la classe interne ou externe, le paramètre de type interne masquerait le paramètre de type externe.

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

Comme il n'existe aucun moyen de faire référence au paramètre de type externe, le compilateur génère un avertissement dans cette situation.

Lorsque des types génériques imbriqués construits sont nommés, le paramètre de type pour le type externe n'est pas inclus dans la liste de paramètres de type pour le type interne, bien que le type interne soit implicitement paramétrable par le paramètre de type externe du type.dans le cas ci-dessus, un nom d'un type construit serait <int> externe : : <chaîne> interne.

L'exemple suivant illustre la génération et la lecture une liste liée à imbriqué dans les classes génériques.

propriétés, événements, indexeurs et opérateurs dans les classes génériques

  • Les propriétés, des événements, des indexeurs et des opérateurs peuvent utiliser des paramètres de type de la classe générique englobante comme valeurs de retour, paramètres, ou variables locales, par exemple lorsque ItemType est un paramètre de type d'une classe :

    public ItemType MyProperty {}
    
  • Les propriétés, des événements, des indexeurs et les opérateurs ne peuvent pas eux-mêmes être paramétrables.

Structures génériques

Les règles pour déclarer et utiliser des structures génériques sont identiques à ceux pour les classes génériques, à l'exception de les différences près notées dans en Visual C++ référence du langage.

Voir aussi

Autres ressources

Génériques (extensions du composant C++)