Introdução a Generics em C#
Generics (tipos genéricos) — uma das capacidades mais poderosas da linguagem C#, introduzida no .NET Framework 2.0. Eles permitem criar classes, métodos e interfaces que funcionam com qualquer tipo de dado, mantendo ao mesmo tempo a tipagem forte. Neste artigo, vamos analisar para que servem generics em C#, como usá-los e quais vantagens eles oferecem ao desenvolvedor.
Imagine que você precisa escrever um método para trocar os valores de duas variáveis. Sem generics, você teria que criar métodos separados para cada tipo: SwapInt, SwapString, SwapDouble e assim por diante. Com generics, essa tarefa é resolvida com uma única função universal.
Principais Vantagens dos Generics
1. Segurança de tipos em tempo de compilação
Generics garantem a verificação de tipos durante a compilação, e não durante a execução. Isso significa que erros relacionados à incompatibilidade de tipos serão detectados no início do desenvolvimento.
// Sem generics - possível erro em tempo de execuçãoArrayList list = new ArrayList();list.Add(123);list.Add("string");int number = (int)list[1]; // InvalidCastException!
// Com generics - erro em tempo de compilaçãoList<int> numbers = new List<int>();numbers.Add(123);numbers.Add("string"); // Erro de compilação!2. Performance
O uso de generics evita operações de boxing e unboxing para tipos por valor. Isso aumenta significativamente a performance ao trabalhar com coleções.
// Boxing/Unboxing - lentoArrayList list = new ArrayList();list.Add(42); // boxingint value = (int)list[0]; // unboxing
// Generics - rápido, sem boxingList<int> list = new List<int>();list.Add(42); // sem boxingint value = list[0]; // sem unboxing3. Reutilização de código
Uma classe ou método genérico pode funcionar com qualquer tipo de dado, eliminando a duplicação de código.
Criação e Uso de Generics
Classes genéricas
Vamos ver um exemplo de criação de uma classe genérica para armazenar um par de valores:
public class Pair<TFirst, TSecond>{ public TFirst First { get; set; } public TSecond Second { get; set; } public Pair(TFirst first, TSecond second) { First = first; Second = second; } public void Display() { Console.WriteLine($"First: {First}, Second: {Second}"); }}
// Usovar pair1 = new Pair<int, string>(1, "um");var pair2 = new Pair<string, double>("PI", 3.14159);
pair1.Display(); // First: 1, Second: umpair2.Display(); // First: PI, Second: 3.14159Métodos genéricos
Métodos também podem ser genéricos. Aqui está um exemplo clássico — o método de troca de valores:
public static void Swap<T>(ref T a, ref T b){ T temp = a; a = b; b = temp;}
// Usoint x = 5, y = 10;Swap<int>(ref x, ref y);Console.WriteLine($"x = {x}, y = {y}"); // x = 10, y = 5
string s1 = "Hello", s2 = "World";Swap(ref s1, ref s2); // o tipo pode ser omitidoConsole.WriteLine($"s1 = {s1}, s2 = {s2}"); // s1 = World, s2 = HelloInterfaces genéricas
Interfaces com generics permitem criar contratos flexíveis:
public interface IRepository<T>{ T GetById(int id); void Add(T entity); void Update(T entity); void Delete(int id);}
public class UserRepository : IRepository<User>{ public User GetById(int id) { // Implementação de obtenção de usuário return new User(); } public void Add(User user) { // Implementação de adição } public void Update(User user) { // Implementação de atualização }