Restrições em parâmetros de tipo genérico (C++/CLI)
Em declarações de tipo genérico ou de método, você pode qualificar um parâmetro de tipo com restrições. Uma restrição é um requisito que os tipos usados como argumentos de tipo devem atender. Um exemplo de restrição seria a necessidade do argumento de tipo de implementar uma certa interface ou herdar de uma classe específica.
Restrições são opcionais; não especificar uma restrição em um parâmetro é o mesmo que usar uma restrição Object.
Sintaxe
where
type-parameter
:
constraint-list
Parâmetros
type-parameter
Um dos identificadores de parâmetro de tipo a ser restrito.
constraint-list
Uma lista separada por vírgulas das especificações de restrição. A lista pode incluir as interfaces a serem implementadas por type-parameter
.
Ela também pode incluir uma classe. Para atender a uma restrição de classe base, o argumento de tipo deve ser da mesma classe que a restrição ou derivar da restrição. Especifique ref class
para indicar que o argumento de tipo deve ser um tipo de referência, incluindo qualquer tipo class
, interface
, delegate
ou array
. Especifique value class
para indicar que o argumento de tipo deve ser um tipo de valor. Qualquer valor de tipo com exceção de Nullable<T>
pode ser especificado.
Você também pode especificar gcnew()
para indicar que o argumento de tipo deve ter um construtor público sem parâmetros.
Você também pode especificar um parâmetro genérico como restrição. O argumento de tipo fornecido para o tipo que você está restringindo deve ser do tipo da restrição ou derivar dele. Esse parâmetro é chamado de restrição de tipo naked.
Comentários
A cláusula da restrição é composta porwhere
,seguido por um parâmetro de tipo, dois pontos:
e a restrição, que especifica a natureza da restrição no parâmetro de tipo. where
é uma palavra-chave contextual. Saiba mais em Palavras-chave contextuais. Separe várias cláusulas where
com um espaço.
As restrições são aplicadas aos parâmetros de tipo para colocar limitações sobre os tipos que podem ser usados como argumentos para um tipo ou método genérico.
As restrições de classe e de interface especificam que os tipos de argumento devem ser ou herdar de uma classe especificada ou implementar uma interface especificada.
A aplicação de restrições a um tipo ou método genérico permite que o código nesse tipo ou método tire proveito dos recursos conhecidos dos tipos restritos. Por exemplo, você pode declarar uma classe genérica de modo que o parâmetro de tipo implemente a interface IComparable<T>
:
// generics_constraints_1.cpp
// compile with: /c /clr
using namespace System;
generic <typename T>
where T : IComparable<T>
ref class List {};
Essa restrição exige que um argumento de tipo usado para T
implemente IComparable<T>
em tempo de compilação. Também permite que os métodos de interface, tais como CompareTo
, sejam chamados. Nenhuma conversão é necessária em uma instância do parâmetro de tipo para chamar métodos de interface.
Os métodos estáticos na classe do argumento de tipo não podem ser chamados por meio do parâmetro de tipo; eles só podem ser chamados por meio do tipo nomeado real.
Uma restrição não pode ser um tipo de valor, incluindo tipos internos, como int
ou double
. Como os tipos de valor não podem ter classes derivadas, apenas uma classe poderia atender à restrição. Nesse caso, o genérico pode ser reescrito com o parâmetro de tipo substituído pelo tipo de valor específico.
As restrições são necessárias em alguns casos, pois o compilador não permitirá o uso de métodos ou de outros recursos de um tipo desconhecido, a menos que as restrições indiquem que o tipo desconhecido é compatível com métodos ou interfaces.
É possível especificar várias restrições para o mesmo parâmetro de tipo em uma lista separada por vírgulas
// generics_constraints_2.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;
generic <typename T>
where T : List<T>, IComparable<T>
ref class List {};
Com vários parâmetros de tipo, use uma cláusula where para cada parâmetro de tipo. Por exemplo:
// generics_constraints_3.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;
generic <typename K, typename V>
where K: IComparable<K>
where V: IComparable<K>
ref class Dictionary {};
Use restrições em seu código de acordo com as regras a seguir:
Se houver várias restrições listadas, as restrições poderão estar listadas em qualquer ordem.
As restrições também podem ser tipos de classe, como classes base abstratas. No entanto, as restrições não podem ser tipos de valor ou classes
sealed
.As restrições não podem ser parâmetros de tipo, mas podem envolver os parâmetros de tipo em um tipo construído aberto. Por exemplo:
// generics_constraints_4.cpp // compile with: /c /clr generic <typename T> ref class G1 {}; generic <typename Type1, typename Type2> where Type1 : G1<Type2> // OK, G1 takes one type parameter ref class G2{};
Exemplos
O exemplo a seguir demonstra como usar restrições para chamar métodos de instância em parâmetros de tipo.
// generics_constraints_5.cpp
// compile with: /clr
using namespace System;
interface class IAge {
int Age();
};
ref class MyClass {
public:
generic <class ItemType> where ItemType : IAge
bool isSenior(ItemType item) {
// Because of the constraint,
// the Age method can be called on ItemType.
if (item->Age() >= 65)
return true;
else
return false;
}
};
ref class Senior : IAge {
public:
virtual int Age() {
return 70;
}
};
ref class Adult: IAge {
public:
virtual int Age() {
return 30;
}
};
int main() {
MyClass^ ageGuess = gcnew MyClass();
Adult^ parent = gcnew Adult();
Senior^ grandfather = gcnew Senior();
if (ageGuess->isSenior<Adult^>(parent))
Console::WriteLine("\"parent\" is a senior");
else
Console::WriteLine("\"parent\" is not a senior");
if (ageGuess->isSenior<Senior^>(grandfather))
Console::WriteLine("\"grandfather\" is a senior");
else
Console::WriteLine("\"grandfather\" is not a senior");
}
"parent" is not a senior
"grandfather" is a senior
Quando um parâmetro de tipo genérico for usado como restrição, será chamado de restrição de tipo naked. As restrições de tipo naked são úteis quando uma função membro com parâmetro de tipo próprio tiver que restringir esse parâmetro ao parâmetro de tipo do tipo recipiente.
No exemplo a seguir, T
é uma restrição de tipo naked no contexto do método Add
.
Também é possível usar restrições de tipo naked em definições de classe genérica. A utilidade das restrições de tipo naked classes genéricas é limitada, pois o compilador não pode presumir nada sobre a restrição de tipo naked, exceto que deriva de Object. Use as restrições de tipo naked em classes genéricas em cenários nos quais você deseja impor uma relação de herança entre dois parâmetros de tipo.
// generics_constraints_6.cpp
// compile with: /clr /c
generic <class T>
ref struct List {
generic <class U>
where U : T
void Add(List<U> items) {}
};
generic <class A, class B, class C>
where A : C
ref struct SampleClass {};