Tipos Genéricos no .Net Framework
Veja neste artigo como utilizar tipos genéricos no .NET Framework, para criação de métodos e classes que necessitam trabalhar com argumentos de tipos inicialmente desconhecidos.
O Generics do .NET Framework implementaram a possibilidade de parâmetros receberem tipos desconhecidos até o momento da execução do método. Os métodos que tratam tipos genéricos desmistificam suas características apenas no momento em que ele estiver sendo utilizado. Além de métodos, também é possível a geração de classes complexas e totalmente genéricas, permitindo escrever códigos que atendam necessidades comuns em um projeto, sem perder a confiabilidade em conversões, boxing ou qualquer outra necessidade fundamental.
Curso relacionado: Curso de .NET Framework
Geralmente os tipos genéricos estão identificados com a letra T, em maiúsculo, mas é uma letra convencionalmente utilizada em teste e não obrigatoriamente é necessário utilizá-la.
Utilizando tipos genéricos no .NET Framework
A declaração de um tipo genérico .NET Framework é simples. Como o exemplo a seguir:
Listagem 1: Declarando método genérico
public void MetodoGenerico<T>(T input)
{
}
Com o identificado <T>, definimos o tipo que será incluído nesse método. Ao declarar o valor para T na chamada do método, o parâmetro de entrada assumrá automaticamente o mesmo tipo. Ele não precisa obrigatoriamente ser preenchido.
Listagem 2: Chamando Método Genérico no .NET Framework
public void ChamandoMetodoGenerico()
{
var program = new Program();
var inteiro = 2;
float flutuante = 2.3F;
MetodoGenerico(program);
MetodoGenerico(inteiro);
MetodoGenerico<float>(flutuante);
}
Simples assim, a declaração de classes não tem muitas diferenças na sua declaração, utilizamos a mesma concepção.
Listagem 3: Classe Genérica no .NET Framework
public class Generics<T>
{
public Generics()
{
}
public Generics(T input)
{
var prop = input;
}
}
Então, para utilizar uma classe que utiliza genérics, pode-se proceder da seguinte forma:
Listagem 4: Chamando uma classe genérica
public void ChamandoClasseGenerica()
{
Generics<Program> objProgram = new Generics<Program>();
Generics<int> objInt = new Generics<int>(10);
}
Repare que agora o valor para <T> é obrigatório, pois é preciso informar qual será o tipo do objeto instanciado.
As interfaces de classes também podem ser genéricas, a declaração é idêntica à das classes.
A declaração de classes genéricas pode ser feita de forma restritiva, permitindo assim que os tipos que a classe poderá receber durante a sua instância sejam controlados.
Utilizando o operador where é possível realizar esse controle, definindo regras para a declaração do tipo genérico T. A abaixo vemos um exemplo básico de uso do where, onde foi definido que o tipo T deverá, obrigatoriamente, ser uma classe.
Listagem 5: Receber apenas classes
public class Generics<T> where T : class
{
public Generics()
{
}
public Generics(T input)
{
var prop = input;
}
}
Da mesma forma, por exemplo, é possível estabelecer que uma classe genérica só receba o tipo struct para o argumento T. Existe também a possibilidade de definir que o tipo T tem de herdar de uma interface, como a IDisposable, por exemplo. Assim, quando eu declarar um tipo para a classe, o interpretador irá verificar se o mesmo combina com as opções definidas pela classe genérica.
Listagem 6: Classe genérica com tipos e interfaces
public class Generics<T> where T : class, IDisposable, IObserver<T>, new()
{
public Generics()
{
}
public Generics(T input)
{
var prop = input;
}
}
}
A classe genérica não é limitada a receber apenas um tipo, um exemplo é a classe Dictionary do framework. Essa classe possibilita receber dois tipos e os mesmos são armazenados em um array de duas posições.
A declaração para esse tipo é feita da seguinte forma:
Listagem 7: Classe genérica com dois parâmetros
public class Generics<T, I>
{
public Generics()
{
}
public Generics(T input)
{
var prop = input;
}
public Generics(T input, I input2)
{
var prop = input;
var prop2 = input2;
}
}
Os conceitos de definição de regras par ao tipo T, vistos anteriormente, podem também serem aplicados agora. Por exemplo, para cada tipo de entrada informado para a classe pode ser utilizado o operador where, como podemos ver abaixo.
Listagem 8: Classe com dois parâmetros e operadores
public class Generics<T, I>
where T : System.NullReferenceException, IConvertible
where I : struct, ICloneable
{
public Generics()
{
}
public Generics(T input)
{
var prop = input;
}
public Generics(T input, I input2)
{
var prop = input;
var prop2 = input2;
}
}
Codificar
Abaixo temo um exemplo de código que realiza a conversão de tipos, utilizando método de extensão. Nele é capturado o tipo definido para o argumento T e o tipo da variável que está sendo passada.
Listagem 9: Converter Genérico
public static T To<T>(this IConvertible obj)
{
try
{
Type TypeReturn = typeof(T);
if (TypeReturn.IsGenericType && (TypeReturn.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
if (obj == null || (string)obj == string.Empty)
{
return (T)(object)null;
}
return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(TypeReturn));
}
return (T)Convert.ChangeType(obj, TypeReturn);
}
catch
{
return default(T);
}
}
Ele captura o valor para conversão pelo método de extensão e o T é o tipo para o qual ele deseja que o valor seja convertido. Existe uma validação para os tipos Nullable, caso se deseje converter um True ou False para o tipo Boolean?, por exemplo, também é possível.
Conclusão
Os tipos genéricos têm várias vantagens e utilizações. A reutilização de código é o que mais é chamativo. Permite que funções que seriam repetitivas, por causa dos tipos que seriam passados para elas, se tornem o menor dos problemas.
Essa prática exige certo cuidado, pois o compilador só apontará o erro no momento que o código estiver sendo executado, então é importante tomar cuidado com alguns convertes que não sejam de tipos seguros.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo