Generics in .NET (Genéricos no .NET)

Os genéricos permitem que você personalize um método, uma classe, uma estrutura ou uma interface para o tipo exato de dados no qual ele atua. Por exemplo, em vez de usar a classe Hashtable, que permite que as chaves e os valores sejam de qualquer tipo, você pode usar a classe genérica Dictionary<TKey,TValue> e especificar os tipos permitidos para a chave e o valor. Entre os benefícios de genéricos estão maior reutilização de códigos e segurança de tipos.

Definindo e usando genéricos

Genéricos são classes, estruturas, interfaces e métodos que possuem espaços reservados (parâmetros de tipo) para um ou mais dos tipos que eles armazenam ou usam. Uma classe de coleção genérica pode usar um parâmetro de tipo como um espaço reservado para o tipo de objetos que ela armazena; os parâmetros de tipo aparecem como os tipos de seus campos e os tipos de parâmetro de seus métodos. Um método genérico pode usar seu parâmetro de tipo como o tipo de seu valor de retorno ou como o tipo de um de seus parâmetros formais. O código a seguir ilustra uma definição de classe genérica simples.

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

Quando você cria uma instância de uma classe genérica, pode especificar os tipos reais para substituir os parâmetros de tipo. Isso estabelece uma nova classe genérica, conhecida como uma classe genérica construída, com seus tipos escolhidos substituídos em todos os locais em que aparecem os parâmetros de tipo. O resultado é uma classe fortemente tipada que é personalizada para sua escolha de tipos, como mostra o código a seguir.

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Terminologia de genéricos

Os seguintes termos são usados para discutir genéricos no .NET:

  • A definição de tipo genérico é uma classe, estrutura ou declaração de interface que funciona como um modelo, com espaços reservados para os tipos que ela pode conter ou usar. Por exemplo, a classe System.Collections.Generic.Dictionary<TKey,TValue> pode conter dois tipos: chaves e valores. Como uma definição de tipo genérico é apenas um modelo, não é possível criar instâncias de uma classe, estrutura ou interface que seja uma definição de tipo genérico.

  • Parâmetros de tipo genérico ou parâmetros de tipo são os espaços reservados em uma definição de método ou de tipo genérico. O tipo genérico System.Collections.Generic.Dictionary<TKey,TValue> tem dois parâmetros de tipo, TKey e TValue, que representam os tipos de suas chaves e valores.

  • Um tipo genérico construído ou um tipo construído é o resultado de especificação de tipos para os parâmetros de tipo genérico de uma definição de tipo genérico.

  • Um argumento de tipo genérico é qualquer tipo que seja substituído por um parâmetro de tipo genérico.

  • O termo geral tipo genérico inclui definições de tipo genérico e de tipos construídos.

  • Covariância e contravariância de parâmetros de tipo genérico permitem que você use tipos genéricos construídos cujos argumentos de tipo sejam mais derivados (covariância) ou menos derivados (contravariância) de um tipo construído de destino. A covariância e a contravariância são referidas coletivamente como variância. Para obter mais informações, consulte Covariância e contravariância.

  • Restrições são limites colocados em parâmetros de tipo genérico. Por exemplo, você pode limitar um parâmetro de tipo a tipos que implementam a interface genérica System.Collections.Generic.IComparer<T>, para garantir que instâncias do tipo possam ser classificadas. Você também pode restringir parâmetros de tipo a tipos que tenham uma determinada classe base, que tenham um construtor sem parâmetros ou que sejam tipos de referência ou tipos de valor. Os usuários do tipo genérico não podem substituir argumentos de tipo que não satisfaçam as restrições.

  • A definição de método genérico é um método com duas listas de parâmetros: uma lista de parâmetros de tipo genérico e uma lista de parâmetros formais. Os parâmetros de tipo podem aparecer como o tipo de retorno ou como os tipos de parâmetros formais, como mostra o código a seguir.

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

Métodos genéricos podem aparecer em tipos genéricos ou não genéricos. É importante observar que um método não é genérico apenas porque ele pertence a um tipo genérico ou até mesmo porque possui parâmetros formais cujos tipos sejam os parâmetros genéricos do tipo de delimitador. Um método é genérico somente se ele tiver sua própria lista de parâmetros de tipo. No código a seguir, somente o método G é genérico.

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

Vantagens e desvantagens de genéricos

Há muitas vantagens no uso de coleções e delegados genéricos:

  • Segurança de tipo. Genéricos deslocam a carga de segurança de tipos do seu local até o compilador. Não é necessário escrever código para testar o tipo de dados correto, pois isso é aplicado no tempo de compilação. A necessidade de conversão de tipos e a possibilidade de erros de tempo de execução são reduzidas.

  • Menos código e código que seja reutilizado com mais facilidade. Não é necessário herdar de um tipo base e substituir membros. Por exemplo, o LinkedList<T> está pronto para uso imediato. Por exemplo, você pode criar uma lista vinculada de cadeias de caracteres com a seguinte declaração de variável:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Melhor desempenho. Tipos de coleções genéricas geralmente executam melhor para armazenar e manipular tipos de valor, pois não é necessário colocar os tipos de valor em caixa.

  • Delegados genéricos permitem retornos fortemente tipados sem a necessidade de criar múltiplas classes de delegados. Por exemplo, o delegado genérico Predicate<T> permite criar um método que implementa seus próprios critérios de pesquisa para um tipo específico e usar seu método com método do tipo Array, tais como Find, FindLast e FindAll.

  • Genéricos simplificam o código gerado dinamicamente. Quando você usa genéricos com código gerado dinamicamente, não é necessário gerar o tipo. Isso aumenta o número de cenários nos quais você pode usar métodos dinâmicos leves, em vez de gerar assemblies inteiros. Para obter mais informações, confira Como definir e executar métodos dinâmicos e DynamicMethod.

Veja a seguir algumas limitações de genéricos:

  • Os tipos genéricos podem ser derivados da maioria das classes base, tais como MarshalByRefObject (e restrições podem ser usadas para exigir que parâmetros do tipo genérico derivem de classes base como MarshalByRefObject). No entanto, o .NET não dá suporte a tipos genéricos vinculados a um contexto. Um tipo genérico pode ser derivado de ContextBoundObject, mas tentar criar uma instância desse tipo causa uma TypeLoadException.

  • Enumerações não podem ter parâmetros do tipo genérico. Uma enumeração pode ser genérica somente incidentalmente (por exemplo, porque ela está aninhada em um tipo genérico que é definido usando Visual Basic, C# ou C++). Para saber mais, confira "Enumerações" em Common Type System.

  • Métodos dinâmicos leves não podem ser genéricos.

  • No Visual Basic, C# e C++, um tipo aninhado que está embutido em um tipo genérico não pode ser instanciado, a menos que os tipos tenham sido atribuídos aos parâmetros de tipo de todos os tipos de delimitadores. Outra maneira de dizer isso é que em reflexão, um tipo aninhado que é definido usando essas linguagens inclui os parâmetros de tipo de todos os seus tipos de delimitadores. Isso permite que os parâmetros de tipos de delimitadores sejam usados nas definições de membro de um tipo aninhado. Para obter mais informações, consulte "Tipos aninhados" em MakeGenericType.

    Observação

    Um tipo aninhado que é definido pela emissão do código em um assembly dinâmico ou usando o Ilasm.exe (IL Assembler) não é necessário para incluir parâmetros de tipo de seus tipos de delimitadores; no entanto, se ele não os incluir, os parâmetros de tipo não estarão no escopo na classe aninhada.

    Para obter mais informações, consulte "Tipos aninhados" em MakeGenericType.

Biblioteca de classes e suporte ao idioma

O .NET fornece várias classes de coleção genérica nos seguintes namespaces:

Interfaces genéricas para a implementação de comparações de classificação e de igualdade são fornecidas no namespace System, juntamente com tipos de delegados genéricos para manipuladores de eventos, conversões e predicados de pesquisa.

Foi adicionado suporte para genéricos para o namespace System.Reflection para examinar tipos genéricos e métodos genéricos, para System.Reflection.Emit para emitir assemblies dinâmicos que contenham tipos e métodos genéricos e para System.CodeDom para gerar gráficos de origem que incluem genéricos.

O Common Language Runtime fornece novos opcodes e prefixos para oferecer suporte a tipos genéricos na Microsoft Intermediate Language (MSIL), incluindo Stelem, Ldelem, Unbox_Any, Constrained e Readonly.

Visual C++, C# e Visual Basic todos oferecem suporte completo para definir e usar genéricos. Para saber mais sobre suporte à linguagem, confira Tipos genéricos no Visual Basic, Introdução aos genéricos e Visão geral dos genéricos no Visual C++.

Tipos e genéricos aninhados

Um tipo que é aninhado em um tipo genérico pode depender dos parâmetros de tipo do tipo genérico de delimitador. O Common Language Runtime considera tipos aninhados como genéricos, mesmo que eles não tenham seus próprios parâmetros de tipo genérico. Quando você cria uma instância de um tipo aninhado, deverá especificar argumentos de tipo para todos os tipos de delimitadores.

Título Descrição
Coleções genéricas no .NET Descreve as classes de coleção genérica e outros tipos genéricos no .NET.
Delegados genéricos para manipulação de matrizes e listas Descreve delegados genéricos para conversões, predicados de pesquisa e ações a serem tomadas nos elementos de uma matriz ou coleção.
Interfaces genéricas Descreve interfaces genéricas que fornecem funcionalidade comum entre famílias de tipos genéricos.
Covariância e Contravariância Descreve covariância e contravariância em parâmetros de tipo genérico.
Tipos de Coleção de Uso Comum Fornece informações de resumo sobre as características e os cenários de uso dos tipos de coleção no .NET, incluindo tipos genéricos.
Quando Usar Coleções Genéricas Descreve regras gerais para determinar quando usar tipos de coleção genérica.
Como: Definir um tipo genérico com a emissão de reflexão Explica como gerar assemblies dinâmicos que incluem tipos e métodos genéricos.
Tipos genéricos no Visual Basic Descreve o recurso genérico para usuários do Visual Basic, incluindo tópicos de instruções para uso e definição de tipos genéricos.
Introdução aos genéricos Fornece uma visão geral da definição e do uso de tipos genéricos para usuários do C#.
Visão geral de genéricos no Visual C++ Descreve o recurso de genéricos para usuários do C++, incluindo as diferenças entre genéricos e modelos.

Referência

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes