Classes genéricas (C++/CLI)
Uma classe genérico foi declarado usando o seguinte formato:
[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 termos a seguir são usados:
attributes (opcional)
Declarativa informações adicional. Para obter mais informações sobre os atributos e das classes de atributo, consulte atributos.classe chave
class ou typenametype-parameter-identifier(s),
Lista separada por vírgula de identificadores que especificam os nomes dos parâmetros de tipo.restrições cláusulas
Uma lista separada por vírgulas (não) de cláusulas de where que especificam as restrições para os parâmetros de tipo. Assume a forma:where type-parameter-identifier : constraint-list ...
restrições lista
classe-ou- interface,[ …]acessibilidade-modificadores
Modificadores de acessibilidade para a classe genérico. Para Tempo de Execução do Windows, o único modificador permitido é private. Para Common Language Runtime, os modificadores permitidos são private e public.identificador
O nome da classe genérico, qualquer identificador válido do C++.modificadores (opcional)
Os modificadores permitidos incluem sealed e abstract.a lista
Uma lista que contém a uma classe base e todas as interfaces implementadas, qualquer separada por vírgulas.corpo classe
O corpo da classe, que contém campos, funções de membro, etc.declarators
Alguns declarações de variáveis desse tipo. Por exemplo:identificadorde ^, […]
Você pode declarar classes genéricas como esses (observe que a palavra-chave class pode ser usado em vez de typename). Neste exemplo, ItemType, KeyType e ValueType são os tipos desconhecidos que são especificados no ponto onde o tipo. HashTable<int, int> é um tipo construído de tipo genérico HashTable<KeyType, ValueType>. Um número de tipos diferentes podem ser construídos construídos a partir de um único tipo genérico. Os tipos construídos construídos das classes genéricas são tratados como qualquer outro tipo de classe de referência.
// 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 (tipos internos como int ou double, ou tipos de valores definidos pelo usuário) e os tipos de referência podem ser usados como um argumento de tipo genérico. A sintaxe na definição genérico é a mesma de qualquer maneira. Sintaticamente, o tipo desconhecido é tratado como se fosse um tipo de referência. No entanto, o tempo de execução pode determinar quais se o tipo realmente usado é um tipo de valor e substitua o código gerado apropriado para o acesso direto aos membros. Os tipos de valores usados como argumentos genéricas de tipo não são boxed e assim que não sofrem a caneta de desempenho associada ao com. A sintaxe usada no corpo de genérico deve ser T^ e '->' em vez de '.'. O uso de ref new, gcnew (Extensões de Componentes C++) para o parâmetro de tipo será interpretado corretamente em tempo de execução como a criação simples de um tipo de valor se o argumento de tipo for um tipo de valor.
Você também pode declarar uma classe genérica com Restrições em parâmetros de tipo genérico (C++/CLI) em tipos que podem ser usadas para o parâmetro de tipo. No exemplo qualquer tipo usado para ItemType deve implementar a interface de IItem . Tentando usar int, por exemplo, que não implementa IItem, gerará um erro de tempo de compilação como o argumento do tipo não atender à restrição.
// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};
As classes genéricas no mesmo namespace não podem ser sobrecarregadas somente alterando o número ou os tipos de parâmetros de tipo. No entanto, se cada classe residir em um namespace diferente, podem ficar sobrecarregados. Por exemplo, considere as duas classes, MyClass e MyClass<ItemType>, nos namespaces A e B. As duas classes podem ser sobrecarregadas em uma terceira namespace C: 2.0
// 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
}
};
}
A classe base e interfaces de base não poderão ser parâmetros de tipo. No entanto, a classe base pode envolver o parâmetro de tipo como um argumento, como nos seguintes casos:
// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};
generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};
Os construtores e os destruidores são executados uma vez para cada instância do objeto (como de costume); os construtores estáticos são executados uma vez para cada tipo construído.
Campos em classes genéricas
Essa seção a seguir demonstra o uso de campos da instância e estáticos em classes genéricas.
Variáveis de instância
As variáveis de instância de uma classe genérico podem ter os tipos e os inicializadores variáveis que incluem todos os parâmetros de tipo incluindo da classe.
Exemplo
No exemplo, três instâncias diferentes da classe genérico, MyClassItemType<>, são criadas usando os argumentos apropriados do tipo (int, double, e 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);
}
O exemplo a seguir demonstra o uso de campos estáticos e um construtor estática dentro de uma classe genérico.
// 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 método não genérico, ProtectData, dentro de uma classe genérico, MyClass<ItemType>. O método usa o parâmetro de tipo ItemType de classes na assinatura em um tipo construído aberto.
// 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 método não genérico ainda é genérico no sentido de que é parametrizada pelo parâmetro de tipo de classe, mas não tem nenhum parâmetro de tipo adicional.
Todos os tipos de métodos em classes genéricas podem ser genéricos, incluindo a digitação estática, a instância do, e métodos virtuais.
O exemplo a seguir demonstra como declarar e usar métodos genéricos dentro das 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 declarações de uma propriedade da instância do em uma classe genérico.
// 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 exemplo a seguir mostra uma classe com um evento genérico.
// 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 estrutura genérica, MyGenStruct, com um campo, myField, e atribui valores de tipos diferentes (int, double, String^) para 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
Na criação de um novo tipo genérico, novas instâncias de todas as variáveis estáticas são criadas e qualquer construtor estático para esse tipo é executado.
As variáveis estáticas podem usar todos os parâmetros de tipo incluindo da classe.
Métodos em classes genéricas
Os métodos em classes genéricas podem ser genéricas próprios; os métodos não genéricas serão implicitamente com parâmetros pelo parâmetro de tipo da classe.
As seguintes regras especiais se aplicam aos métodos nas classes genéricas:
Os métodos em classes genéricas podem usar parâmetros de tipo como parâmetros, tipos de retorno, ou variáveis locais.
Os métodos em classes genéricas podem usar tipos construídos aberto ou fechado como parâmetros, tipos de retorno, ou variáveis locais.
Métodos não genéricas em classes genéricas
Os métodos genéricos em classes que não têm nenhum parâmetro de tipo adicional são referidos normalmente porque não genérico embora sejam implicitamente com parâmetros incluindo genérico pela classe.
A assinatura de um método não genérico pode incluir um ou mais parâmetros de tipo de classe, inclusive diretamente ou em um tipo construído aberto. Por exemplo:
void MyMethod(MyClass<ItemType> x) {}
O corpo desses 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 em classes genéricas e não genéricas. Por exemplo:
Usar aninhada em classes genéricas
Como com classes comuns, você pode declarar outros tipos em uma classe genérico. A instrução aninhada da classe com parâmetros é implicitamente pelos parâmetros de tipo de instrução externa da classe. Portanto, uma classe aninhada distinta é definida para cada tipo 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<>Outerint::Inner não é o mesmo que o tipo<>Outerdouble::Inner.
Como com métodos genéricos em classes genéricos, os parâmetros de tipo adicionais podem ser definidos para o tipo aninhado. Se você usar os mesmos nomes de parâmetro de tipo na classe interna e externa, o parâmetro de tipo interno ocultará o parâmetro de tipo externa.
// 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 fazer referência ao parâmetro de tipo externo, o compilador gerará um aviso nessa situação.
Quando os tipos genéricos aninhados construídos é chamado, o parâmetro de tipo do tipo exterior não está incluído na lista de parâmetros de tipo do tipo interno, mesmo que o tipo interno é implicitamente com parâmetros pelo parâmetro do tipo externa. No caso acima, um nome de um tipo será<>construído Outerint::Innerstring<>.
O exemplo a seguir demonstra a criação e à leitura usando uma lista vinculada de aninhada em classes genéricas.
Propriedades, eventos, indicadores e operadores em classes genéricas
As propriedades, os eventos, os indicadores e os operadores podem usar os parâmetros de tipo genérico da classe inclusive como valores de retorno, parâmetros, ou variáveis locais, como quando ItemType é um parâmetro de tipo de uma classe:
public ItemType MyProperty {}
As propriedades, os eventos, os indicadores e os operadores em si não podem ser parametrizadas.
Structs genérico
As regras para declarar e usar estruturas genéricas são iguais às de classes genéricos, com exceção das diferenças observadas na referência de linguagem Visual C++.