Classes genéricas (Guia de Programação em C#)

Classes genéricas encapsulam as operações que não são específicas para um tipo de dados específico. O uso mais comum para classes genéricas é com coleções como listas vinculadas, tabelas de hash, pilhas, filas, árvores e assim por diante. Operações como, por exemplo, adicionando e removendo itens da coleção são executadas basicamente da mesma maneira independentemente do tipo de dados armazenados.

Para a maioria dos cenários que exigem as coleções de classes, a abordagem recomendada é usar os fornecidos na.Biblioteca de classes. Para obter mais informações sobre como usar essas classes, consulte Genéricos na biblioteca de classes .NET Framework (Guia de Programação em C#).

Normalmente, você deve criar classes genéricas começando com uma classe concreta existente e, em seguida, alterando os tipos em parâmetros de tipo um por vez até atingir o equilíbrio ideal de generalização e usabilidade. Ao criar suas próprias classes genéricas, considerações importantes incluem o seguinte:

  • Quais tipos de generalizar em parâmetros de tipo.

    Como uma regra, quanto mais você pode parametrizar tipos, o mais flexíveis e reutilizáveis se torna seu código. No entanto, há muita generalização pode criar código que é difícil para outros desenvolvedores ler ou compreender.

  • Que restrições, se houver, para aplicar os parâmetros de tipo (consulte Restrições a parâmetros de tipo (Guia de Programação em C#)).

    Uma boa regra é aplicar restrições máxima possíveis que permitirá ainda que lidam com os tipos que você deve tratar. Por exemplo, se você souber que sua classe genérica é destinado ao uso somente com os tipos de referência, aplica a restrição de classe. Que impedirá o uso não intencional de sua classe com os tipos de valor e permitirá que você use o as operador em Te verifique se há valores nulos.

  • Se fator comportamento genérico em subclasses e classes base.

    Porque classes genéricas podem servir como classes base, as mesmas considerações de design se aplicam aqui como ocorre com as classes não-genéricas. Consulte as regras sobre a herança de classes genéricas de base neste tópico.

  • Decida se deseja implementar uma ou mais interfaces genéricas.

    Por exemplo, se você estiver criando uma classe que será usada para criar itens em uma coleção baseado em genéricos, você pode ter que implementar uma interface, como IComparable onde T é o tipo de sua classe.

Para obter um exemplo de uma classe genérica simples, consulte Introdução aos genéricos (Guia de Programação em C#).

As regras para parâmetros de tipo e restrições tem várias implicações para o comportamento da classe genérica, especialmente com relação de herança e membro de acessibilidade. Antes de prosseguir, você deve entender alguns termos. Para uma classe genérica Node<T>, código do cliente pode referenciar a classe ou especificando um argumento de tipo, para criar um tipo construído fechado (Node<int>). Como alternativa, ele pode deixar o parâmetro de tipo não for especificado, por exemplo, quando você especifica uma classe genérica de base criar uma abertura construído tipo (Node<T>). Classes genéricas podem herdar de concretos, fechadas construídos ou abrir construídos classes base:

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type 
class NodeConcrete<T> : BaseNode { }

//closed constructed type 
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type  
class NodeOpen<T> : BaseNodeGeneric<T> { }

Não genérico, em outras palavras, as classes concretas, pode herdar de classes base construídos fechadas, mas não a partir de classes construídos abertos ou parâmetros de tipo porque não há nenhuma maneira em tempo de execução de código do cliente fornecer o argumento do tipo necessário para instanciar a classe base.

//No error 
class Node1 : BaseNodeGeneric<int> { }

//Generates an error 
//class Node2 : BaseNodeGeneric<T> {} 

//Generates an error 
//class Node3 : T {}

Classes genéricas que herdam de tipos construídos abertos devem fornecer argumentos de tipo para qualquer parâmetro de tipo de classe base que não é compartilhado pela classe herdando, conforme demonstrado no código a seguir:

class BaseNodeMultiple<T, U> { }

//No error 
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error 
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error 
//class Node6<T> : BaseNodeMultiple<T, U> {} 

Classes genéricas que herdam de tipos construídos abertos devem especificar restrições que são um superconjunto de ou implica, as restrições no tipo base:

class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

Tipos genéricos podem usar vários parâmetros de tipo e restrições, da seguinte maneira:

class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

Abrir tipo construído construído e fechado pode ser usado como parâmetros do método:

void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

Se uma classe genérica implementa uma interface, todas as instâncias dessa classe podem ser convertidas em interface.

Classes genéricas são invariável. Em outras palavras, se um parâmetro de entrada especifica um List<BaseClass>, você obterá um erro de tempo de compilação se você tentar fornecer um List<DerivedClass>.

Consulte também

Referência

Genéricos (Guia de Programação em C#)

System.Collections.Generic

Conceitos

Guia de Programação em C#

Outros recursos

Salvar o estado de enumeradores

Um quebra-cabeça da herança, parte 1