Olá
Se você é daqueles, principalmente desenvolvedores que vêm de versões antigas do Delphi, que ao escutar o termo “Generics” sente aquele calafrio (brrr!), saiba que você não está sozinho!
Para tentar amenizar um pouco esta sensação, vou mostrar um exemplo bem simples de uso de generics (brrr! :)), e assim você verá que trata-se de um ótimo recurso que acompanha o Delphi desde a versão 2009.
Na verdade, calafrios mesmo eu sinto toda vez que abro o meu XE2 trial:
[hiena Hardy ON]
Nove dias… tanta coisa pra falar, quantos recursos ainda por abordar, quantas descobertas por fazer… Ha! Como passa rápido! 🙁
[hiena Hardy OFF]
Bom, continuando… a maneira mais fácil de mostrar o generics em funcionamento é utilizar a classe TList. Para isso, crie um novo projeto e no form um button:
Agora, crie uma classe chamada TCidade:
[sourcecode language=”delphi”]
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TCidade = class
private
FUF: string;
FNome: string;
FCodIbge: string;
procedure SetCodIbge(const Value: string);
procedure SetNome(const Value: string);
procedure SetUF(const Value: string);
published
public
property CodIbge: string read FCodIbge write SetCodIbge;
property Nome: string read FNome write SetNome;
property UF: string read FUF write SetUF;
end;
[/sourcecode]
No evento OnClick do Button, instancie uma lista (sem ser generic) e o objeto Cidade:
[sourcecode language=”delphi”]
procedure TForm4.Button1Click(Sender: TObject);
var
Lista: TList;
Cidade: TCidade;
begin
Lista := TList.Create;
Cidade := TCidade.Create;
try
finally
Lista.Free;
Cidade.Free;
end;
end;
[/sourcecode]
Com os nossos objetos criados, devemos setar as propriedades da Cidade e adicioná-la na lista:
[sourcecode language=”delphi”]
procedure TForm4.Button1Click(Sender: TObject);
var
Lista: TList;
Cidade: TCidade;
begin
Lista := TList.Create;
Cidade := TCidade.Create;
try
// setando propriedades
Cidade.CodIbge := ‘2101400’;
Cidade.Nome := ‘Balsas’;
Cidade.UF := ‘MA’;
// adicionando na lista
Lista.Add(Cidade);
finally
Lista.Free;
Cidade.Free;
end;
end;
[/sourcecode]
Note que ao adicionar a cidade à lista, verificamos que o parâmetro é do tipo Pointer.
O código acima apenas adiciona uma cidade na lista mas não mostra qualquer mensagem ou indicação de que nossa lista agora tem uma cidade. Vamos então, mostrar ao usuário o nome da cidade na tela para confirmar a inserção:
[sourcecode language=”delphi”]
// adicionando na lista
Lista.Add(Cidade);
// mensagem ao usuário do sistema
ShowMessage(‘A cidade foi adicionada na lista ‘ + Lista.Items[0]);
finally
Lista.Free;
Cidade.Free;
end;
[/sourcecode]
Ops! Se compilar da forma que está, dará um erro:
[DCC Error] ufrmprinc.pas(58): E2010 Incompatible types: ‘string’ and ‘Pointer’
A lista está devolvendo um Pointer e o showmessage requer uma string. Para resolver o problema, faremos o typecast:
Com typecasting, agora é possível ver as propriedades e métodos da classe TCidade. Iremos então mostrar o nome da cidade:
[sourcecode language=”delphi”]
procedure TForm4.Button1Click(Sender: TObject);
var
Lista: TList;
Cidade: TCidade;
begin
Lista := TList.Create;
Cidade := TCidade.Create;
try
// setando propriedades
Cidade.CodIbge := ‘2101400’;
Cidade.Nome := ‘Balsas’;
Cidade.UF := ‘MA’;
// adicionando na lista
Lista.Add(Cidade);
// mensagem ao usuário do sistema
ShowMessage(‘A cidade foi adicionada na lista: ‘ + TCidade(Lista.Items[0]).Nome);
finally
Lista.Free;
Cidade.Free;
end;
end;
[/sourcecode]
Executando a aplicação e clicando no botão vemos o nome da cidade, confirmando assim, a inserção na lista:
Bom, até agora nada de generics. Mas antes de entrarmos no assunto, vamos analisar melhor o código acima.
Vemos que foi necessário fazer um typecasting para obtermos o nome da cidade que estava dentro da lista. Isso é um exemplo bem simples e não observamos qualquer tipo de problema no que foi feito. Porém, imagine se fosse um grande sistema, com centenas de classes, cada uma com suas propriedades e métodos. Neste cenário, seria muito fácil obter erros durante a utilização do sistema, visto que uma lista pode comportar diversos objetos.
Mas aí você poderia dizer: basta fazer o teste antes, como por exemplo:
[sourcecode language=”delphi”]
if (Cidade is TCidade) then
ShowMessage(‘A cidade foi adicionada na lista: ‘ + TCidade(Lista.Items[0]).Nome);
[/sourcecode]
Ok, resolveria. Mas você teria que ter mais essa preocupação, ou seja, teria que ficar testando sempre que necessitasse ter certeza de que o objeto se refere à determinada classe em seu sistema, principalmente se este objeto vier de um parâmetro passado como TObject (vemos muito isso nos eventos, onde se utiliza sender como parâmetro).
Faremos mais um teste criando uma nova classe:
[sourcecode language=”delphi”]
TCliente = class
private
FIdade: integer;
FNome: string;
procedure SetIdade(const Value: integer);
procedure SetNome(const Value: string);
published
public
property Nome: string read FNome write SetNome;
property Idade: integer read FIdade write SetIdade;
end;
[/sourcecode]
No Onclick do botão, adicionamos mais um objeto, o Cliente:
[sourcecode language=”delphi”]
var
Lista: TList;
Cidade: TCidade;
Cliente : TCliente;
begin
Lista := TList.Create;
Cidade := TCidade.Create;
Cliente := TCliente.Create;
try
// setando propriedades
Cidade.CodIbge := ‘2101400’;
Cidade.Nome := ‘Balsas’;
Cidade.UF := ‘MA’;
Cliente.Nome := ‘Joao’;
Cliente.Idade := 18;
// adicionando na lista a cidade
Lista.Add(Cidade);
// adicionando na lista o cliente
Lista.Add(Cliente);
// mensagem ao usuário do sistema
ShowMessage(‘A cidade foi adicionada na lista: ‘ + TCidade(Lista.Items[0]).Nome);
// mensagem ao usuário do sistema
ShowMessage(‘A cidade foi adicionada na lista: ‘ + TCidade(Lista.Items[1]).Nome);
finally
Lista.Free;
Cidade.Free;
cliente.Free;
end;
end;
[/sourcecode]
Adicionamos o cliente à lista e executamos o showmessage, porém, atente para o detalhe que mantive o mesmo typecasting. Ao executarmos a aplicação, não teremos qualquer erro, mostrando tanto o nome da cidade quanto o nome do cliente. Se na classe TCliente não existisse a propriedade “Nome”, o resultado seria nulo e a mensagem seria: A cidade foi adicionada na lista:
Ou seja, não teria o nome. Como a classe TCliente tem a propriedade, retorna o nome do cliente.
Neste caso, se quiséssemos uma lista cujo conteúdo constasse apenas dados de cidades, isto se configuraria em um erro em nosso sistema. Para evitar este tipo de situação, utilizaremos generics e assim, nossa lista será mais específica.
O primeiro passo é adcionar à cláusula uses a unit: Generics.Collections
Depois, devemos alterar a nossa lista no OnClick do botão:
[sourcecode language=”delphi”]
var
Lista: TList<TCidade>;
begin
Lista := TList<TCidade>.create;
[/sourcecode]
Colocamos entre menor que e maior que o tipo de dado que nossa lista irá trabalhar.
Veja que agora, ao adicionarmos um objeto na lista, o parâmetro mudou de Pointer para TCidade:
Neste momento, se compilarmos a aplicação teríamos um erro, visto que agora a lista comporta somente objetos da classe TCidade. Portanto, teremos que excluir a adição do cliente:
[sourcecode language=”delphi”]
var
Lista: TList<TCidade>;
Cidade: TCidade;
Cliente : TCliente;
begin
Lista := TList<TCidade>.Create;
Cidade := TCidade.Create;
Cliente := TCliente.Create;
try
// setando propriedades
Cidade.CodIbge := ‘2101400’;
Cidade.Nome := ‘Balsas’;
Cidade.UF := ‘MA’;
Cliente.Nome := ‘Joao’;
Cliente.Idade := 18;
// adicionando na lista a cidade
Lista.Add(Cidade);
// mensagem ao usuário do sistema
ShowMessage(‘A cidade foi adicionada na lista: ‘ + Lista.Items[0].Nome);
finally
Lista.Free;
Cidade.Free;
cliente.Free;
end;
end;
[/sourcecode]
Um detalhe importante: nossa lista não requer mais typecasting. Podemos acessar diretamente as propriedades e métodos da classe TCidade: lista.items[0].Nome. Isto irá também facilitar a codificação e uma melhor visualização do que está sendo feito. Percebeu o ganho que temos quando utilizamos corretamente o conceito?
Você poderá criar suas próprias classes e métodos parametrizados e como este artigo é apenas uma leve introdução ao assunto, sugiro que estude a classe TList que fica em Generics.Collections e acesse o http://docwiki.embarcadero.com/RADStudio/en/Overview_of_Generics
Abraços.
Contato:luiz_sistemas@hotmail.com
Twitter:twitter.com/luiz_sistemas
No caso o generics ja fez na declaracao de List a conversao de tipos?
E teriamos que criar uma lista especifica para cada classe?
1- Com o Generics, já deixamos explícito o tipo de dado que será trabalhado na lista. Desta forma, não há mais a necessidade de typecasting, pois a lista já é específica da classe que definimos entre o menor que e maior que.
2- Sim. No exemplo, o sistema deveria trabalhar apenas com cidades. Então, a lista aceitando um cliente seria um erro, não é mesmo?! Com o generics acabamos com o trabalho de ter que ficar testando se a classe é a que realmente queremos trabalhar. Se no caso, você desejasse ter uma lista de clientes, aí sim, poderíamos criar uma lista de clientes, como por exemplo:
[sourcecode language=”delphi”]
var
Lista: TList<TCliente>;
begin
Lista := TList<TCliente>.create;
…
[/sourcecode]
Agora, se a lista não tivesse essa a necessidade de ser específica, poderíamos utilizar a TList normalmente.
Lembrando que, Generics são tipos e métodos que podem ser parametrizados. Por métodos, entenda-se Procedures e Functions genéricas.
Veja que com o uso de Generics, você acaba tornando o código mais seguro, visto que você sabe em tempo de compilação com que tipo de dado está trabalhando. Se tentar fazer alguma coisa que vai de encontro com a declaração da classe, imediatamente notará, pois não conseguirá compilar.
No exemplo ficou especifico, mais em uma aplicação real, poderia abstrair e usar interface, aí não ficaria tão especifico. É um grande ganho o Generics e o TList na linguagem.
Luiz parabéns pela matéria.
Correto! Realmente, seguindo os princípios OO (programe para uma interface não para uma implementação), este seria um uso interessante e recomendável.
Neste exemplo, a ideia era mesmo restringir a lista, aceitando apenas cidades, e ao mesmo tempo simplificar o exemplo.
Obrigado pela participação!