Tipos Genéricos (Generics) (POO)

Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
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.

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil.
Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software.
Para saber mais ... http://desenvolvedores.net/marcelo
[]'s

Você vai gostar de...

Postagens populares.