Desenvolvimento - C#
Generics com C# e .NET 2.0
Desde a versão 2.0 do .NET Framework, onde além de grandes novidades como Generics e Nullable Types, foram incluídos alguns delegates poderosíssimos que explicarei com mais detalhes logo adiante.
por Rodolfo PaoniDesde a versão 2.0 do .NET Framework, onde além de grandes novidades como Generics
e Nullable Types, foram incluídos alguns delegates
poderosíssimos que explicarei com mais detalhes logo adiante. Acredito que a
maioria dos desenvolvedores já estejam familiarizados com Generics,
mas sempre vale a pena “refrescar a memória” com os conceitos. Farei um pequeno
paralelo entre a versão 1.1 e 2.0 para que possamos observar claramente a sua
evolução.
Quando trabalhávamos com .NET 1.1 normalmente desenvolvíamos uma classe que
herdasse de CollectionBase para que tivéssemos tipagem forte ao invés
de utilizar ArrayLists puros, que também eram (e ainda são) muito
utilizados.
Imaginemos uma classe Usuario:
public class Usuario
{
private string _nome;
public string Nome
{
get { return
this._nome; }
set {
this._nome = value; }
}
private string _grupo;
public string Grupo
{
get { return
this._grupo; }
set {
this._grupo = value; }
}
public override string ToString()
{
return
"Nome: " + Nome + " - Grupo: " + Grupo;
}
}
E a seguinte coleção de Usuários:
public class UsuarioCollection : CollectionBase
{
public int Add(Usuario u)
{
return base.List.Add(u);
}
public bool Contains(Usuario u)
{
return base.List.Contains(u);
}
public void Insert(int index, Usuario u)
{
base.List.Insert(index, u);
}
public Usuario this[int index]
{
get { return (Usuario)base.List[index]; }
set { base.List[index] = value; }
}
public void Remove(Usuario u)
{
base.List.Remove(u);
}
}
Uso da coleção
Usuario u1 = new Usuario();
u1.Nome = "Rodolfo";
u1.Grupo = "A";
Usuario u2 = new Usuario();
u2.Nome = "Rodrigo";
u2.Grupo = "B";
Usuario u3 = new Usuario();
u3.Nome = "Ronald";
u3.Grupo = "C";
UsuarioCollection uc = new UsuarioCollection();
uc.Add(u1);
uc.Add(u2);
uc.Add(u3);
// uc.Add(“Rodolfo”); // Erro em tempo de compilação
Console.WriteLine("Nome: " + uc[0].Nome);
foreach (Usuario u in uc)
{
Console.WriteLine(u.ToString());
}
Repare que apesar de termos tipagem forte, ainda temos problemas com relação a
Casting, pois internamente ainda utilizamos um objeto do tipo ArrayList e temos
uma grande perda de produtividade, pois é necessário cria uma classe para casa
tipo de coleção desejado.
Utilizando Generics
Podemos criar uma classe genérica da seguinte forma:
class GenericClass<T>
{
private T _valor;
public T Valor
{
get { return this._valor; }
set { this._valor = value; }
}
}
T é o tipo que será atribuído à classe e ela terá uma property ‘Valor’
deste mesmo tipo. Repare que não existe Casting na recuperação dos
dados, pois o compilador já sabe qual é o tipo atribuído à classe. Suponhamos
uma classe Cliente com uma propriedade ‘Nome’ e um construtor sobrecarregado.
Poderemos utilizar a classe ‘List’ em ‘System.Collections.Generic’ e definir o
tipo da lista no momento da declaração.
Utilizando uma classe genérica
GenericClass<int> t1 = new GenericClass<int>();
t1.Valor = 10;
Console.WriteLine("Inteiro: " + t1.Valor);
GenericClass<string> t2 = new GenericClass<string>();
t2.Valor = "10";
Console.WriteLine("String: " + t2.Valor);
Utilizando List<T>
List<Cliente> clientes = new List<Cliente>();
clientes.Add(new Cliente("Rodolfo"));
clientes.Add(new Cliente("João"));
clientes.Add(new Cliente("Maria"));
//Erro em tempo de compilação, pois a lista foi definida como uma lista de
"Cliente"
//clientes.Add(new Funcionario("Rodolfo"));
//Recuperando as informações com "foreach"
foreach (Cliente c in clientes)
{
Console.WriteLine("Nome: " + c.Nome);
}
Console.WriteLine("\n");
//Recuperando as informações com "Iterator"
IEnumerator<Cliente> enumer = clientes.GetEnumerator();
while (enumer.MoveNext())
{
Console.WriteLine("Nome: " +
enumer.Current.Nome);
}
Console.WriteLine(string.Empty);
Temos também as chamadas Constraints em Generics onde
podemos definir certas restrições neste tipo T atribuído no momento da
declaração de uma classe genérica, como no exemplo em ‘GenericClass’. No
exemplo acima a restrição é que o tipo T deve ser um tipo ‘Cliente’. Podemos
também definir outros tipos de restrição:
where T : struct – Onde T seja uma estrutura
where T : class – Onde T seja uma classe
where T : new() – Onde T possua um construtor default
where T : U – Onde T herde de U
where T : <nome da classe> – Onde T herde de uma classe
específica
where T : <nome da interface> – Onde T implemente uma interface
Exemplo:
class GenericClass<T> where T : Cliente // Onde T herde de "Cliente"
{
private T _valor;
public T Valor
{
get { return this._valor; }
set { this._valor = value; }
}
}
Por enquanto é só. No próximo artigo abordarei alguns delegates muito
úteis, que apesar de fazerem parte do .NET Framework 2.0, ainda são pouco
utilizados.
Até a próxima!