Tipos Genéricos (Generics)
Tipos genéricos ou programação genérica é simples de entender.
Imagine que você não saiba o tipo de objeto que a sua classe, método ou propriedade vai receber como parâmetro ou retorno, neste caso você pode usar Generics.
Vamos pegar como exemplo uma Janela, eu não sei o tipo de janela que irei ter, ela pode ser uma veneziana, uma janela basculante ou uma janela de correr, logo vou declarar a minha classe como “Generics” vejamos como fazer isso:
namespace Desenvolvedores.Net.ExemploGenerics
{
class Janela<T>
{
public Janela()
{
/*
* aqui temos que instanciar a nossa propriedade
* Tipo com um valor default.
* para que ao acessarmos a mesma não retorne erro.
*/
Tipo = (T)Activator.CreateInstance<T>();
}
//podemos usar no construtor
public Janela(T _tipo)
{
/*
* aqui não precisamos fazer como foi feito no construtor acima,
* pois já estamos passando o tipo
*/
Tipo = _tipo;
}
//na propriedade
public T Tipo { get; set; }
public bool Aberta { get; set; }
public bool Quebrada { get; set; }
public void Quebrar()
{
Quebrada = true;
}
public void Consertar()
{
Quebrada = false;
}
public void Abrir()
{
Aberta = true;
}
public void Fechar()
{
Aberta = false;
}
//como tipo de parâmetro em métodos
public void SetTipo(T value)
{
Tipo = value;
}
//como retorno em métodos
public T GetTipo()
{
return Tipo;
}
}
}
No código acima, reparem na linha selecionada a declaração está assim:
class Janela<T> , este <T> identifica que iremos ter um tipo genérico para a classe, logo, se usramos em uma herança as classes filhas terão que informar qual o tipo que iremos usar.
Janela do tipo Euroglass.
namespace Desenvolvedores.Net.ExemploGenerics
{
class Euroglass : Janela<Euroglass>
{
}
}
Percebam também que podemos usar o tipo <T> em qualquer lugar da nossa classe, Métodos, Propriedades, Construtores.
Vejamos abaixo as declarações de nossas classes de janelas que iremos usar para testes.
Janela Basculante:
namespace Desenvolvedores.Net.ExemploGenerics
{
public enum TipoEixo
{
Horizontal,
Vertical
}
class Basculante
{
public TipoEixo Eixo { get; set; }
}
}
Janela Veneziana:
namespace Desenvolvedores.Net.ExemploGenerics
{
public enum TipoLamina
{
Fixa,
Movel
}
class Veneziana
{
public TipoLamina Lamina { get; set; }
}
}
Janela de Correr:
namespace Desenvolvedores.Net.ExemploGenerics
{
class DeCorrer
{
public void Correr()
{
Console.WriteLine("A Janela correu");
}
}
}
Agora vamos a um exemplo de uso:
Crie uma aplicação console em C#.
e cole o código abaixo:
namespace Desenvolvedores.Net.ExemploGenerics
{
class Program
{
static void Main(string[] args)
{
//Aqui iremos declarar uma janela basculante
Janela<Basculante> basculante = new Janela<Basculante>();
//veja que ao acessar a propriedade Tipo,
//teremos a propriedade Eixo, que vem do tipo Basculante
if (basculante.Tipo.Eixo == TipoEixo.Horizontal)
Console.WriteLine("Janela basculante com eixo horizontal");
else
Console.WriteLine("Janela basculante com eixo vertical");
//vamos agora a outro tipo de janela, janela de correr
Janela<DeCorrer> correr = new Janela<DeCorrer>();
//chamaremos o método que existe no tipo de janela Correr
correr.Tipo.Correr();
//aqui modificaremos um pouco a chamada
//para ver o uso do construtor
Veneziana venez = new Veneziana();
venez.Lamina = TipoLamina.Movel;
Janela<Veneziana> veneziana = new Janela<Veneziana>(venez);
if (veneziana.Tipo.Lamina == TipoLamina.Fixa)
Console.WriteLine("Janela veneziana com lâminas fixas");
else
Console.WriteLine("Janela veneziana com lâminas móveis");
//modificaremos o tipo de lâmina para teste
veneziana.Tipo.Lamina = TipoLamina.Fixa;
if (veneziana.Tipo.Lamina == TipoLamina.Fixa)
Console.WriteLine("Janela veneziana com lâminas fixas");
else
Console.WriteLine("Janela veneziana com lâminas móveis");
Console.ReadKey();
}
}
}
Terminamos aqui a explicação básica do tipo genérico, vamos agora ver alguns tópicos avançados. Do jeito que nossa classe janela foi declarada ela aceita qualquer tipo de objeto, ou seja, podemos declarar a nossa janela assim:
Janela<Porta> porta = new Janela<Porta>();
Mas eu não quero permitir que a nossa Janela seja uma porta, logo temos que restringir o tipo de objetos que <T> irá aceitar, deste modo podemos usar a declaração where, que irá nos ajudar a restringir o tipo que irá ser usado em <T>.
Para isso, primeiro temos que criar uma interface, é ela que iremos usar como filtro para os tipos.
namespace Desenvolvedores.Net.ExemploGenerics
{
interface IJanela
{
}
}
Após criado a interface, modificamos a nossa classe Janela para restringir o uso de de tipos em <T>. Veja:
namespace Desenvolvedores.Net.ExemploGenerics
{
class Janela<T> where T : IJanela
{
public Janela()
{
/*
* aqui temos que instanciar a nossa propriedade
* Tipo com um valor default.
* para que ao acessarmos a mesma não retorne erro.
*/
Tipo = (T)Activator.CreateInstance<T>();
}
//podemos usar no construtor
public Janela(T _tipo)
{
/*
* aqui não precisamos fazer como foi feito no construtor acima,
* pois já estamos passando o tipo
*/
Tipo = _tipo;
}
//na propriedade
public T Tipo { get; set; }
public bool Aberta { get; set; }
public bool Quebrada { get; set; }
public void Quebrar()
{
Quebrada = true;
}
public void Consertar()
{
Quebrada = false;
}
public void Abrir()
{
Aberta = true;
}
public void Fechar()
{
Aberta = false;
}
//como tipo de parâmetro em métodos
public void SetTipo(T value)
{
Tipo = value;
}
//como retorno em métodos
public T GetTipo()
{
return Tipo;
}
}
}
Veja na linha selecionada a declaração class Janela<T> where T : IJanela o uso de where T : IJanela, onde usamos o where, isso indica que só iremos aceitar objetos que implementem a interface IJanela.
Logo se compilarmos o código de exemplo iremos ter erros do tipo:
The type ‘Desenvolvedores.Net.ExemploGenerics.Basculante’ cannot be used as type parameter ‘T’ in the generic type or method ‘Desenvolvedores.Net.ExemploGenerics.Janela<T>’. There is no implicit reference conversion from ‘Desenvolvedores.Net.ExemploGenerics.Basculante’ to ‘Desenvolvedores.Net.ExemploGenerics.IJanela’.
E assim para todos os outros tipos, para isso temos que corrigir as declarações de nossas classes. Veja:
Janela Basculante:
namespace Desenvolvedores.Net.ExemploGenerics
{
public enum TipoEixo
{
Horizontal,
Vertical
}
class Basculante: IJanela
{
public TipoEixo Eixo { get; set; }
}
}
Janela Veneziana:
namespace Desenvolvedores.Net.ExemploGenerics
{
public enum TipoLamina
{
Fixa,
Movel
}
class Veneziana: IJanela
{
public TipoLamina Lamina { get; set; }
}
}
Janela de Correr:
namespace Desenvolvedores.Net.ExemploGenerics
{
class DeCorrer: IJanela
{
public void Correr()
{
Console.WriteLine("A Janela correu");
}
}
}
Pronto. Isso fará com que as nossas classes de tipo de Janelas possam ser usadas em nossa Janela<T>, e não permitirá mais o uso do tipo Porta.
Repare que todas elas implementam a interface IJanela. Podemos também usar generics para determinar o tipo de retorno em um método ou para determinar o tipo de parâmetro.
Veja:
namespace Desenvolvedores.Net.ExemploGenerics
{
class Generics
{
//retorno com um tipo Genérico
public T RetornarTipoGenerico<T>()
{
int x = 10;
int y = 10;
object ret = x + y;
ret = Convert.ChangeType(ret, typeof(T));
return (T)ret;
}
//tipo de parâmetro genérico
public void ParametroGenerico<T>(T _Param1)
{
Console.WriteLine("O parâmetro 1 e do tipo " +
_Param1.GetType().ToString() +
". E seu valor é de " + _Param1.ToString());
}
}
}
Reparem que aqui eu não declarei na classe, e sim no declaração do método. Exemplo de uso:
static void Main(string[] args)
{
Generics g = new Generics();
Console.WriteLine(g.RetornarTipoGenerico<string>());
g.ParametroGenerico<double>(4);
Console.ReadKey();
}
Podemos ter múltiplas declaracões de genéricos, veja:
class Janela<T, K>
{
}
E podemos também restringir seus tipos:
class Janela<T, K>
where T : IJanela
where K : ICloneable
{
}
E isto vale para os métodos também.
/// <summary>
/// Exemplo de múltiplos genéricos
/// </summary>
/// <typeparam name="R">define o retorno do método</typeparam>
/// <typeparam name="P">define o tipo de parâmetro para o método
/// </typeparam>
/// <param name="param1">parâmetro 1</param>
/// <returns>R</returns>
public R MultiplosGenericos<R, P>(P param1)
{
return (R)new object();
}
O intuito deste artigo foi introduzir o conceito de generics, com o tempo você ira descobrir que seu uso é bastante útil. |