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:

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;

No evento OnClick do Button, instancie uma lista (sem ser generic) e o objeto Cidade:

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;

Com os nossos objetos criados, devemos setar as propriedades da Cidade e adicioná-la na lista:

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;

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:

    // 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;

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:

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;

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:

if (Cidade is TCidade) then
  ShowMessage('A cidade foi adicionada na lista: ' + TCidade(Lista.Items[0]).Nome);

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:

  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;

No Onclick do botão, adicionamos mais um objeto, o Cliente:

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;

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:

var
  Lista: TList<TCidade>;
begin
  Lista := TList<TCidade>.create;

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:

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;

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.

Desenvolve softwares desde 1995
Luiz Carlos

Contato:luiz_sistemas@hotmail.com
Twitter:twitter.com/luiz_sistemas

Desenvolvedor de software desde 1995. Em 1998, abriu sua própria empresa, a Lukas Sistemas, desde então passou a atender diversas empresas, principalmente autopeças. Apaixonado por Delphi, porém não o impede de flertar com outras linguagens sempre que possível. Mora na cidade de Balsas/MA com sua esposa e dois filhos.

5 thoughts on “Dica: Generics (brrr!!!)”

    1. 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:

      var
        Lista: TList<TCliente>;
      begin
        Lista := TList<TCliente>.create;
      
      ...
      

      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.

      1. 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!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.