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 outypenametipo - 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.
Variá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.
Mé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++.