Generics em C#: guia completo de uso e vantagens

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Generics em C#: para que servem e como usar



Tipos genéricos (Generics) são um dos recursos principais da linguagem C#, que permite criar classes, métodos e interfaces com tipos parametrizados. Em vez de escrever o mesmo código para cada tipo de dado, você o escreve uma vez e especifica o tipo ao usar. Isso melhora o desempenho, a segurança de tipos e a reutilização de código. Neste artigo, vamos analisar para que servem os Generics, como aplicá-los na prática e ver exemplos reais do .NET.



Para que servem os Generics em C#



Antes do surgimento dos Generics em C# (versão 2.0), os desenvolvedores frequentemente usavam o tipo object e a coleção ArrayList. Isso gerava dois problemas:



  • Perda de desempenho — boxing e unboxing para tipos por valor (int, double, struct).
  • Falta de verificação de tipos em tempo de compilação — você pode acidentalmente colocar uma string em uma lista de números e obter um erro apenas em tempo de execução.


Os Generics resolvem ambos os problemas. Eles permitem criar estruturas de dados type-safe sem custos adicionais desnecessários. Por exemplo, List<int> trabalha com int sem boxing, e o compilador não permitirá adicionar uma string ali.



Conceitos principais dos Generics



Classes genéricas



Uma classe genérica é declarada com um parâmetro de tipo entre colchetes angulares. O parâmetro de tipo é um placeholder que é substituído pelo tipo real ao criar uma instância.



public class Box<T>{    private T _value;

public Box(T value) { _value = value; }

public T GetValue() => _value;}


Uso:



var intBox = new Box<int>(42);var stringBox = new Box<string>("Hello");

Console.WriteLine(intBox.GetValue()); // 42Console.WriteLine(stringBox.GetValue());// Hello


Métodos genéricos



Um método pode ter seu próprio parâmetro de tipo, mesmo que a classe não seja genérica:



public class Utils{    public static void Swap<T>(ref T a, ref T b)    {        T temp = a;        a = b;        b = temp;    }}

// Usoint x = 1, y = 2;Utils.Swap(ref x, ref y);Console.WriteLine($"{x} {y}"); // 2 1


Interfaces genéricas



Interfaces com parâmetro de tipo permitem criar abstrações flexíveis:



public interface IRepository<T>{    T GetById(int id);    void Add(T entity);}

public class UserRepository : IRepository<User>{ public User GetById(int id) => new User(); public void Add(User user) { }}


Restrições (Constraints) em Generics



Às vezes é necessário impor restrições sobre os tipos que podem ser usados. Para isso, usa-se a palavra-chave where.



Principais tipos de restrições



  • where T : class — apenas tipos por referência (string, classes).
  • where T : struct — apenas tipos por valor (int, DateTime, struct).
  • where T : new() — o tipo deve ter um construtor público sem parâmetros.
  • where T : SomeBaseClass — o tipo deve ser ou herdar da classe especificada.
  • where T : ISomeInterface — o tipo deve implementar a interface.


Exemplo com várias restrições:



public class Repository<T> where T : class, IEntity, new(){    public T Create()    {        return new T(); // permitido graças a new()    }}


Covariância e Contravariância em Generics



A partir do C# 4.0, tornou-se possível gerenciar a variância de interfaces genéricas usando as palavras-chave in e out.



  • Covariância (out) — permite usar

Blogs

Book Recommendations