Tipos genéricos no Visual Basic (Visual Basic)

Um tipo genérico é um único elemento de programação que se adapta para executar a mesma funcionalidade para diversos tipos de dados. Ao definir uma classe ou procedimento genérico, você não precisa definir uma versão separada para cada tipo de dados para o qual você talvez queira executar essa funcionalidade.

Uma analogia é um conjunto de chaves de fenda com cabeças removíveis. Inspecione o parafuso que você precisa girar e selecione a cabeça correta para esse parafuso (de fenda, fenda cruzada, torx). Depois de inserir a cabeça correta no cabo da chave de fenda, você executa exatamente a mesma função com a chave de fenda, ou seja, girando o parafuso.

Diagram of a screwdriver set with different heads.

Quando você define um tipo genérico, ele é parametrizado com um ou mais tipos de dados. Isso permite que o código de uso adapte os tipos de dados aos seus requisitos. Seu código pode declarar vários elementos de programação diferentes do elemento genérico, cada um atuando em um conjunto de tipos de dados diferente. Mas todos os elementos declarados executam uma lógica idêntica, independentemente dos tipos de dados sendo usados.

Por exemplo, talvez você queira criar e usar uma classe de fila que opere em um tipo de dados específico, como String. Você pode declarar essa classe de System.Collections.Generic.Queue<T>, como mostra o exemplo a seguir.

Public stringQ As New System.Collections.Generic.Queue(Of String)

Agora você pode usar stringQ para trabalhar exclusivamente com os valores String. Como stringQ é específico para String, em vez de ser generalizado para valores Object, você não tem uma conversão de tipo ou associação tardia. Isso economiza tempo de execução e reduz erros em tempo de execução.

Para obter mais informações sobre como usar um tipo genérico, consulte Como usar uma classe genérica.

Exemplo de uma classe genérica

O exemplo a seguir mostra uma definição de esqueleto de uma classe genérica.

Public Class classHolder(Of t)
    Public Sub processNewItem(ByVal newItem As t)
        Dim tempItem As t
        ' Insert code that processes an item of data type t.
    End Sub
End Class

No esqueleto anterior, t é um parâmetro de tipo, ou seja, um espaço reservado para um tipo de dados que você fornece quando declara a classe. Em outro lugar do código, você pode declarar várias versões de classHolder fornecendo vários tipos de dados para t. O exemplo a seguir mostra duas dessas instruções.

Public integerClass As New classHolder(Of Integer)
Friend stringClass As New classHolder(Of String)

As instruções acima declaram classes construídas, nas quais um tipo específico substitui o parâmetro de tipo. Essa substituição é propagada em todo o código na classe construída. O exemplo a seguir mostra a aparência do procedimento processNewItem no integerClass.

Public Sub processNewItem(ByVal newItem As Integer)
    Dim tempItem As Integer
    ' Inserted code now processes an Integer item.
End Sub

Para obter um exemplo mais completo, consulte Como definir uma classe capaz de fornecer uma funcionalidade idêntica em tipos de dados diferentes.

Elementos de Programação Qualificados

Você pode definir e usar classes genéricas, estruturas, interfaces, procedimentos e delegados. Observe que o .NET Framework define várias classes genéricas, estruturas e interfaces que representam elementos genéricos comumente usados. O namespace System.Collections.Generic fornece dicionários, listas, filas e pilhas. Antes de definir seu próprio elemento genérico, veja se ele está disponível em System.Collections.Generic.

Os procedimentos não são tipos, mas é possível definir e usar procedimentos genéricos. Consulte Procedimentos genéricos no Visual Basic.

Vantagens dos tipos genéricos

Um tipo genérico serve como base para declarar vários elementos de programação diferentes, cada um operando em um tipo de dados específico. As alternativas a um tipo genérico são:

  1. Um único tipo que opera no tipo de dados Object.

  2. Um conjunto de versões específicas ao tipo, cada uma codificada individualmente e operando em um tipo de dados específico, como String, Integer ou um tipo definido pelo usuário, como customer.

Um tipo genérico tem as seguintes vantagens em relação a essas alternativas:

  • Segurança de tipos. Tipos genéricos impõem a verificação de tipo de tempo de compilação. Tipos baseados em Object aceitam qualquer tipo de dados e você deve gravar o código para verificar se um tipo de dados de entrada é aceitável. Com tipos genéricos, o compilador pode detectar incompatibilidades de tipo antes do tempo de execução.

  • Desempenho. Tipos genéricos não precisam realizar a operação de box e unbox dos dados, pois cada um deles é especializado em um tipo de dados. As operações baseadas em Object devem realizar a operação de box dos tipos de dados de entrada para convertê-los em Object e realizar a operação de unbox dos dados destinados à saída. As conversões boxing e unboxing reduzem o desempenho.

    Os tipos com base em Object também são associados tardiamente, o que significa que o acesso a seus membros requer código extra no tempo de execução. Isso também reduz o desempenho.

  • Consolidação de código. O código em um tipo genérico deve ser definido apenas uma vez. Um conjunto de versões específicas de tipo de um tipo deve replicar o mesmo código em cada versão, sendo a única diferença o tipo de dados específico para essa versão. Com tipos genéricos, as versões específicas de tipo são geradas do tipo genérico original.

  • Reutilização de código. O código que não depende de um tipo de dados específico pode ser reutilizado com vários tipos de dados, se for genérico. Geralmente, é possível reutilizá-lo mesmo com um tipo de dados não previsto originalmente.

  • Suporte ao IDE. Quando você usa um tipo construído declarado a partir de um tipo genérico, o IDE (ambiente de desenvolvimento integrado) pode oferecer mais suporte enquanto você está desenvolvendo seu código. Por exemplo, o IntelliSense pode mostrar as opções específicas de tipo de um argumento para um construtor ou método.

  • Algoritmos genéricos. Algoritmos abstratos independentes de tipo são bons candidatos para tipos genéricos. Por exemplo, um procedimento genérico que classifica itens usando a interface IComparable pode ser usado com qualquer tipo de dados que implemente IComparable.

Restrições

Embora o código em uma definição de tipo genérico deva ser o mais independente de tipo possível, talvez seja necessário exigir uma determinada funcionalidade de qualquer tipo de dados fornecido ao tipo genérico. Por exemplo, se você quiser comparar dois itens para classificar ou agrupar, o tipo de dados deve implementar a interface IComparable. Você pode impor esse requisito adicionando uma restrição ao parâmetro de tipo.

Exemplo de uma restrição

O exemplo a seguir mostra uma definição de esqueleto de uma classe com uma restrição que requer o argumento de tipo para implementar IComparable.

Public Class itemManager(Of t As IComparable)
    ' Insert code that defines class members.
End Class

Se o código subsequente tentar construir uma classe de itemManager que fornece um tipo que não implementa IComparable, o compilador sinalizará um erro.

Tipos de restrições

A restrição pode especificar os seguintes requisitos em qualquer combinação:

  • O argumento de tipo deve implementar uma ou mais interfaces

  • O argumento de tipo deve ser do tipo ou herdar de, no máximo, uma classe

  • O argumento de tipo deve expor um construtor sem parâmetros acessível ao código que cria objetos a partir dele

  • O argumento de tipo deve ser um tipo de referência ou um tipo de valor

Se você precisar impor mais de um requisito, use uma lista de restrições separada por vírgulas dentro de chaves ({ }). Para exigir um construtor acessível, inclua a palavra-chave Novo operador na lista. Para exigir um tipo de referência, inclua a palavra-chave Class; para exigir um tipo de valor, inclua a palavra-chave Structure.

Para obter mais informações sobre restrições, consulte Tipo de lista.

Exemplo de várias restrições

O exemplo a seguir mostra uma definição de esqueleto de uma classe genérica com uma lista de restrições no parâmetro de tipo. No código que cria uma instância dessa classe, o argumento de tipo deve implementar as interfaces IComparable e IDisposable, ser um tipo de referência e expor um construtor sem parâmetros acessível.

Public Class thisClass(Of t As {IComparable, IDisposable, Class, New})
    ' Insert code that defines class members.
End Class

Termos Importantes

Os tipos genéricos introduzem e usam os seguintes termos:

  • Tipo genérico. Uma definição de uma classe, estrutura, interface, procedimento ou delegado para o qual você fornece pelo menos um tipo de dados ao declará-lo.

  • Parâmetro de tipo. Em uma definição de tipo genérico, um espaço reservado para um tipo de dados fornecido ao declarar o tipo.

  • Argumento de tipo. Um tipo de dados específico que substitui um parâmetro de tipo ao declarar um tipo construído de um tipo genérico.

  • Restrição. Uma condição em um parâmetro de tipo que restringe o argumento de tipo que você pode fornecer. Uma restrição pode exigir que o argumento de tipo deva implementar uma interface específica, ser ou herdar de uma determinada classe, ter um construtor sem parâmetros acessível ou ser um tipo de referência ou um tipo de valor. Você pode combinar essas restrições, mas pode especificar no máximo uma classe.

  • Tipo construído. Uma classe, estrutura, interface, procedimento ou delegado declarado de um tipo genérico fornecendo argumentos de tipo para os parâmetros de tipo.

Confira também