Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Os desenvolvedores usam genéricos o tempo todo no .NET, seja implícita ou explicitamente. Quando você usa o LINQ no .NET, você já notou que está trabalhando com IEnumerable<T>? Ou se já viste uma amostra online de um "repositório genérico" para interagir com bases de dados usando o Entity Framework, viste que a maioria dos métodos retorna IQueryable<T>? Você deve ter se perguntado o que é o T nesses exemplos e por que ele está lá.
Introduzidos pela primeira vez no .NET Framework 2.0, os genéricos são essencialmente um "modelo de código" que permite aos desenvolvedores definir estruturas de dados seguras sem se comprometer com um tipo de dados real. Por exemplo, List<T> é uma coleção genérica que pode ser declarada e usada com qualquer tipo, como List<int>, List<string>ou List<Person>.
Para entender por que os genéricos são úteis, vamos dar uma olhada em uma classe específica antes e depois de adicionar genéricos: ArrayList. No .NET Framework 1.0, os ArrayList elementos eram do tipo Object. Qualquer elemento adicionado à coleção foi convertido sem ser notado em um Object. O mesmo aconteceria ao ler elementos da lista. Esse processo é conhecido como empacotamento e desempacotamento, e afeta o desempenho. Além do desempenho, no entanto, não há como determinar o tipo de dados na lista em tempo de compilação, o que torna o código frágil. Os genéricos resolvem esse problema definindo o tipo de dados que cada instância da lista conterá. Por exemplo, você só pode adicionar inteiros a List<int>, e somente Pessoas a List<Person>.
Genéricos também estão disponíveis em tempo de execução. O tempo de execução sabe que tipo de estrutura de dados você está usando e pode armazená-la na memória de forma mais eficiente.
O exemplo seguinte é um pequeno programa que ilustra a eficiência de conhecer o tipo de estrutura de dados em tempo de execução:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
Este programa produz resultados semelhantes aos seguintes:
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
A primeira coisa que você pode notar aqui é que classificar a lista genérica é significativamente mais rápido do que classificar a lista não genérica. Você também pode notar que o tipo para a lista genérica é distinto ([System.Int32]), enquanto o tipo para a lista não genérica é generalizado. Como o tempo de execução sabe que o genérico List<int> é do tipo Int32, ele pode armazenar os elementos da lista em uma matriz inteira subjacente na memória, enquanto o não genérico ArrayList tem que converter cada elemento da lista em um objeto. Como mostra este exemplo, as conversões extra ocupam tempo e retardam a ordenação da lista.
Uma vantagem adicional do tempo de execução saber o tipo do seu genérico é uma melhor experiência de depuração. Ao depurar um genérico em C#, você sabe que tipo cada elemento está em sua estrutura de dados. Sem os genéricos, você não teria ideia de que tipo cada elemento era.