Compartir a través de


Clases genéricas (C++/CLI)

Una clase genérica se declara con el formato siguiente:

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

Comentarios

En la sintaxis anterior, se utilizan los términos siguientes:

  • attributes (opcional)
    información declarativa adicional.Para obtener más información sobre los atributos y clases de atributos, vea atributos.

  • clase-clave
    class o typename

  • type-parameter-identifier(s),
    lista separada por comas de identificadores que especifican los nombres de los parámetros de tipo.

  • restricción-cláusulas
    Una lista (no separada por comas de cláusulas de where que especifican las restricciones para los parámetros de tipo.Tiene el formato:

    wheretipo-parámetro-identificador:restricción-lista...

  • restricción-lista
    clase-o-interfaz[, ]

  • accesibilidad-modificadores
    Modificadores de accesibilidad para la clase genérica.para Windows en tiempo de ejecución, el único modificador permitido es private.Para Common Language Runtime, los modificadores permitidos son private y public.

  • Identificador
    El nombre de la clase genérica, cualquier identificador de C++ válido.

  • modificadores (opcional)
    Modificadores permitidos son sealed y abstract.

  • base-lista
    Una lista que contiene la una clase base y cualquier interfaz implementada, todo separados por comas.

  • clase-cuerpo
    El cuerpo de la clase, que contiene campos, funciones miembro, etc.

  • declarantes
    Declaraciones de las variables de este tipo.por ejemplo:identificador de^[, …]

Puede declarar clases genéricas como éstos (observe que la palabra clave class se puede utilizar en lugar de typename).En este ejemplo,ItemType, KeyType y ValueType son tipos desconocidos que se especifican en el punto donde el tipo.HashTable<int, int> es un tipo construido de tipo genérico HashTable<KeyType, ValueType>.Varios tipos construidos pueden ser construidos de un solo tipo genérico.Tratan los tipos construidos construidos de clases genéricas como cualquier otro tipo de clase de referencia.

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

Los tipos de valor (tipos integrados como int o double, o tipos de valor definido por el usuario) y los tipos de referencia se pueden utilizar como argumento de tipo genérico.La sintaxis dentro de la definición genérica es el mismo con independencia de que cueste.Sintácticamente, tratan el tipo desconocido como si fuera un tipo de referencia.Sin embargo, el runtime puede determinar si el tipo utiliza realmente es un tipo de valor y sustituir el código generado adecuado para el acceso directo a los miembros.No combinación y por ello no sufren los tipos de valor utilizados como argumentos de tipo genérico la reducción del rendimiento asociada a la conversión boxing.La sintaxis utilizada en el cuerpo de genérico debe ser T^ y “->” en lugar de “.”.Cualquier uso ref new, gcnew (Extensiones de componentes de C++) de para el parámetro de tipo se interpreta correctamente por el tiempo de ejecución como la creación simple de un tipo de valor si el argumento de tipo es un tipo de valor.

También puede declarar una clase genérica con Restricciones de parámetros de tipo genérico (C++/CLI) en los tipos que se pueden utilizar para el parámetro de tipo.En el siguiente tipo de ejemplo utilizado para ItemType debe implementar la interfaz de IItem .Intentar utilizar int, por ejemplo, que no implementa IItem, generaría un error en tiempo de compilación porque el argumento de tipo no cumple la restricción.

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

Las clases genéricas en el mismo espacio de nombres no pueden sobrecargar únicamente cambiando el número o tipos de parámetros de tipo.Sin embargo, si cada clase vive en otro espacio de nombres, se pueden sobrecargar.por ejemplo, considere las dos clases siguientes, MyClass y MyClass<ItemType>, en los espacios de nombres A y B.Las dos clases se podrán sobrecargar en un tercer espacio de nombres 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
      }
   };
}

La clase base y las interfaces base no pueden tener parámetros de tipo.Sin embargo, la clase base puede incluir el parámetro de tipo como argumento, como en el caso siguiente:

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

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

Se ejecutan los constructores y destructores una vez para cada instancia de objeto (como de costumbre); se ejecutan los constructores estáticos una vez para cada tipo construido.

Campos en las clases genérica

Esta sección muestra el uso de la instancia y campos estáticos de clases genéricas.

skef48fy.collapse_all(es-es,VS.110).gifvariables de instancia

Las variables de instancia de una clase genérica pueden tener tipos y los inicializadores de variable que incluyen ningún los parámetros de tipo de la clase envolvente.

Ejemplo

En el ejemplo siguiente, tres diferentes instancias de la clase genérica, MyClass <ItemType> , se crean utilizando los argumentos de tipo adecuados (int, double, y 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);
   }
  

El ejemplo siguiente muestra cómo utilizar campos estáticos y un constructor estático en una clase genérica.

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

El ejemplo siguiente se declara un método no genérico, ProtectData, dentro de una clase genérica, MyClass<ItemType>.El método utiliza el parámetro ItemType de tipo de clase en la firma en un tipo construido abierto.

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

El método no genérico todavía es genérico en el sentido cuyos parámetros por el parámetro de tipo de clase, pero no tiene ningún parámetro de tipo adicional.

Todos los tipos de métodos en clases genéricas pueden ser genéricos, como static, instancia, y métodos virtuales.

El ejemplo siguiente se muestra cómo declarar y utilizar métodos genéricos dentro de clases genéricas:

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

Este ejemplo muestra declaraciones de una propiedad de instancia de una clase genérica.

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

El ejemplo siguiente se muestra una clase genérica con un evento.

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

El ejemplo siguiente declara un struct genérico, MyGenStruct, con un campo, myField, y asigna valores de tipos diferentes (int, double, String^) a este campo.

// 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 estáticas

En la creación de un nuevo tipo genérico, las nuevas instancias de cualquier variable estática se crean y ejecutan un constructor estático para ese tipo.

Las variables estáticas pueden utilizar cualquier parámetro de tipo de la clase envolvente.

Métodos de las clases genérica

Los métodos en clases genéricas pueden ser genéricos propios; los métodos no genéricos se parámetros implícitamente por el parámetro de tipo de clase.

Las reglas especiales siguientes se aplican a los métodos de clases genéricas:

  • Los métodos en clases genéricas pueden utilizar parámetros de tipo como parámetros, tipos de valor devuelto, o variables locales.

  • Los métodos en clases genéricas pueden utilizar tipos construidos abiertos o cerrados como parámetros, tipos de valor devuelto, o variables locales.

skef48fy.collapse_all(es-es,VS.110).gifMétodos No--Generic en las clases genérica

Los métodos de las clases genéricas que no tienen ningún parámetro de tipo adicional se suelen referirse mientras no genérico aunque parámetros implícitamente por la clase genérica que agrega.

La firma de un método no genérico puede incluir uno o más parámetros de tipo de la clase envolvente, directamente o en un tipo construido abierto.Por ejemplo:

void MyMethod(MyClass<ItemType> x) {}

El cuerpo de estos métodos puede utilizar estos parámetros de tipo.

Métodos genéricos en las clases genérica

Puede declarar métodos genéricos en clases genéricas y no genéricas.Por ejemplo:

Mediante tipos anidados en las clases genérica

Igual que con clases normales, puede declarar otros tipos dentro de una clase genérica.La declaración de la clase anidada se parametriza implícitamente los parámetros de tipo de la declaración de clase externa.Así, una clase anidada distinta se define para cada construyó el tipo externo.por ejemplo, en la declaración,

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

El tipo OUTER <int> :: Interno no es el mismo que el tipo OUTER <doble> :: interno.

como con métodos genéricos en clases genéricas, los parámetros de tipo adicionales pueden ser definido para el tipo anidado.Si utiliza los mismos nombres de parámetro de tipo en la clase interna y externa, el parámetro de tipo interno ocultará el parámetro de tipo externo.

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

Dado que no hay forma de hacer referencia al parámetro de tipo exterior, el compilador generará una advertencia en esta situación.

Cuando se llama a los tipos genéricos anidados construidos, el parámetro de tipo para el tipo exterior no se incluye en la lista de parámetros de tipo para el tipo interno, aunque el parámetro de tipo de tipo externo parametriza el tipo interno implícitamente.En el caso anterior, un nombre de un tipo construido sería OUTER <int> :: <cadena> interno.

El ejemplo siguiente muestra cómo compilar y leer una lista vinculada mediante tipos anidados en clases genéricas.

Propiedades, eventos, Indexers y operadores en las clases genérica

  • Las propiedades, los eventos, los indizadores y operadores pueden utilizar los parámetros de tipo de clase genérica que agrega como valores devueltos, parámetros, o las variables locales, por ejemplo cuando ItemType es un parámetro de tipo de una clase:

    public ItemType MyProperty {}
    
  • Las propiedades, los eventos, los indizadores y operadores no pueden propios tener parámetros.

Structs genérico

Las reglas para declarar y utilizar structs genéricos son iguales que las de las clases genéricas, salvo por las diferencias escritas en la referencia del lenguaje de Visual C++.

Vea también

Otros recursos

Genéricos (Extensiones de componentes de C++)