Compartilhar via


Classes genéricas (C + + / CLI)

Uma classe de genérica é declarado usando o seguinte formulário:

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

Comentários

Na sintaxe acima, os seguintes termos são usados:

  • attributes(opcional)
    Informações declarativas adicionais.Para obter mais informações sobre atributos e classes de atributo , consulte atributos.

  • classe-chave
    Um dos class outypename

  • tipo - oparâmetro- oidentificador(s),
    Lista separada por vírgulas dos identificadores de especificar os nomes dos parâmetros de tipo.

  • restrição-cláusulas
    Uma lista (não separado por vírgula) de onde cláusulas especificando as restrições para os parâmetros de tipo.Leva o formulário:

    where tipo - oparâmetro-identificador : restrição-lista de ...

  • restrição-lista
    classe-or-interface, ...

  • acessibilidade-modificadores
    Modificadores de acessibilidade para a classegenérica.Para o Tempo de Execução do Windows, a apenas permitidos modificador é private.Para o Common Language Runtime, os modificadores permitidos são private e public.

  • identificador
    O nome da genérico classe, qualquer válido C++ identificador.

  • modificadores de (opcional)
    Permitido modificadores incluem sealed e abstrata.

  • lista da base
    Uma lista que contém um classe base e qualquer implementou interfaces, todos separados por vírgulas.

  • classe-corpo
    O corpo da classe, que contém campos, funções de membro, etc.

  • declaradores
    Declarações de todas as variáveis desse tipo.For example: ^identificador, ...]

Você pode declarar classes genéricas como essas (Observe que a palavra-chaveclasse pode ser usado em vez de typename). Neste exemplo, ItemType, KeyType e ValueType são tipos desconhecidos que são especificados no ponto onde o tipo.HashTable<int, int>é um tipo construído do tipo genérico HashTable<KeyType, ValueType>.Um número de tipos diferentes de construído pode ser construído a partir de um único tipo genérico.Tipo construído construído a partir de classes genéricas é tratado como qualquer outro tipo de classe 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>();
}

Os dois tipos de valor (tanto internos como tipos int ou double, ou usuário-valor tipos definidos) e tipos de referência podem ser usados como umargumentodo tipo genérico. A sintaxe dentro da definição genérica é que as mesmas independentemente.Sintaticamente, o tipo desconhecido é tratado como se fosse um tipo de referência.No entanto, o tempo de execução é capaz de determinar que, se o tipo usado na verdade é um tipo de valor e substitua o código gerado apropriado para acesso direto aos membros.Tipos de valor usados como argumentos de tipo genérico não in a box e portanto não sofrem a penalidade de desempenho associada à conversão boxing.A sintaxe usada dentro do corpo do genérico deve ser T ^ e '->'em vez de'.'.Qualquer uso de ref new, gcnew (Extensões de Componentes C++) para o tipo de parâmetro será adequadamente interpretado pelo tempo de execução como a criação simple de um tipo de valor se o tipo de argumento é um tipo de valor.

Você pode também declarar uma classe genérica com Restrições no genérico Digite parâmetros (C + + / CLI) nos tipos que podem ser usados para o parâmetrode tipo.No exemplo a seguir qualquer tipo usado para ItemType deve implementar a IItem interface. Tentando usar int, por exemplo, que não implementa IItem, produziria um compilar- erro de tempo porque o tipo de argumento não satisfaz a restrição.

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

Classes genéricas no mesmo namespace não podem ser sobrecarregados, apenas alterando o número ou os tipos de parâmetros de tipo.No entanto, se cada classe residir em um namespacede diferentes, eles podem ser sobrecarregados.Por exemplo, considere as seguintes duas classes, MyClass e MyClass<ItemType>, nos namespaces A e B.As duas classes podem ser sobrecarregadas, em seguida, em um terceiro namespace 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
      }
   };
}

As interfaces classe base e de base não podem ser parâmetros de tipo.No entanto, a classe base pode envolver o parâmetro de tipo como um argumento, como no seguinte maiúsculas e minúsculas:

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

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

Construtores e destrutores são executados uma vez para cada instância do objeto (como de costume); construtores estático são executados uma vez para cada tipo construído.

Campos em Classes genéricas

Esta seção demonstra o uso de campos de instância e estático em classes genéricas.

skef48fy.collapse_all(pt-br,VS.110).gifVariáveis de instância

Variáveis de instância de uma classe de genéricos podem ter tipos e inicializadores de variável que incluem qualquer parâmetro de tipo da classedelimitador.

Exemplo

No exemplo a seguir, três instâncias diferentes da genérico classe, MyClass, <ItemType>, são criadas usando os argumentos de tipo apropriado (int, double, e seqüência de caracteres).

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

O exemplo a seguir demonstra o uso de campos estático e umconstrutor estáticodentro de uma classede genéricos.

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

O exemplo a seguir declara um não-método genérico, ProtectData, dentro de uma classegenérica, MyClass<ItemType>.O método usa o parâmetro de tipo de classe ItemType em sua assinatura em um em aberto construído tipo.

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

O não-método genérico é ainda genérico no sentido de que ele é parametrizado por um parâmetrodo tipo da classe, mas ele tem nenhum parâmetro de tipo adicionais.

Todos os tipos de métodos em classes genéricas podem ser genéricos, incluindo estático, instância e métodos virtuais.

O exemplo a seguir demonstra a declarar e usar métodos genéricos em classes 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 exemplo mostra as declarações de uma instância de propriedade dentro de uma classede genéricos.

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

O próximo exemplo mostra uma classe genérica com um 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);
}

O exemplo a seguir declara uma structgenérica, MyGenStruct, com um único campo, myFielde atribui valores de tipos diferentes (int, double, String ^) a esse 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);
}
  

Variáveis estáticas

Sobre a criação de um novo tipo genérico, novas instâncias de quaisquer variáveis estático são criadas e qualquerconstrutor estáticopara esse tipo é executado.

Variáveis estáticas podem usar qualquer parâmetro de tipo da classedelimitador.

Métodos em Classes genéricas

Métodos em classes genéricas que podem ser genéricos si mesmos; métodos não genéricos serão parametrizados implicitamente pelo parâmetrodo tipo de classe .

A sintaxe especial a seguir regras aplicar aos métodos em classes genéricas:

  • Métodos em classes genéricas podem usar parâmetros de tipo como parâmetros, tipos de retorno ou variáveis locais.

  • Métodos em classes genéricas podem usar a em aberto ou fechada tipo construído como parâmetros, tipos de retorno ou variáveis locais.

skef48fy.collapse_all(pt-br,VS.110).gifMétodos não genéricos em Classes genéricas

Métodos em classes genéricas sem parâmetros de tipo adicionais são geralmente denominados não genérica embora eles são implicitamente parametrizados pela delimitador classegenérica.

A assinatura de um não -método genérico pode incluir um ou mais parâmetros de tipo de delimitador classe, diretamente ou em um em aberto construído tipo.Por exemplo:

void MyMethod(MyClass<ItemType> x) {}

O corpo de tais métodos também pode usar esses parâmetros de tipo.

Métodos genéricos em Classes genéricas

Você pode declarar métodos genéricos nas classes genéricas e não-genéricas.Por exemplo:

Usando tipos aninhados em Classes genéricas

Assim como com as classes comuns, você pode declarar outros tipos dentro de uma classede genéricos.Adeclaração do aninhada classeimplicitamente é parametrizado pelos parâmetros de tipo dadeclaração classedo exterior. Assim, uma aninhada distintas classe é definida para cada tipo de externo construído.Por exemplo, na declaração,

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

O tipo Outer <int>:: interna não é o mesmo que o tipo Outer <double>:: interna.

Como com os métodos genéricos em classes genéricas, parâmetros de tipo adicionais podem ser definidos para o tipo aninhado.Se você usar os mesmos nomes de parâmetro de tipo na classeinternas e externas, o parâmetro do tipo interno será ocultar o parâmetrodo 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
   };
};

Como não há nenhuma maneira de consultar o parâmetrodo tipo externo, o compilador gerará um aviso nessa situação.

Quando o tipo genérico aninhado e construído é nomeado, o tipo de parâmetro para o tipo externo não está incluído na lista de parâmetro de tipo para o tipo interno, mesmo que o tipo interno implicitamente é parametrizado por de tipo externo parâmetro.O maiúsculas e minúsculasacima, um nome de um tipo construído seria externa <int>:: interna <string>.

O exemplo a seguir demonstra a criação e leitura de uma lista vinculada usando tipos aninhados em classes genéricas.

Propriedades, eventos, os indexadores e operadores em Classes genéricas

  • Propriedades, eventos, os indexadores e operadores podem usar os parâmetros de tipo de delimitador classe genérica como valores de retorno, parâmetros ou variáveis locais, como quando ItemType é um parâmetro de tipo de uma classe:

    public ItemType MyProperty {}
    
  • Propriedades, eventos, os indexadores e operadores não podem eles próprios ser parametrizados.

Structs genérico

As regras para declarar e usar estruturas genéricas são as mesmas classes genéricas, exceto pelas diferenças observadas na referência de linguagem do Visual C++.

Consulte também

Outros recursos

Genéricos (Extensões de Componentes C++)