quarta-feira, 2 de janeiro de 2013

Tipos Genéricos no Delphi


Hoje irei falar um pouco sobre tipos genéricos no delphi.
Um tipo genérico no Delphi pode ser definido por qualquer tipo padrão (string, integer, boolean) ou um tipo criado especificamente para sua aplicação.
Como isto é feito???

Defino uma classe Tvalor onde T é o tipo que a classe irá implementar.
Exemplo de Classe genérica:
TValor = class
FValor: T;
end;

Exemplo de utilização da classe:
Procedure teste();
Var
oTexto: Tvalor;
begin
oTexto := TValor.Create;
try
oTexto.Valor := ‘isto é um teste’;
finally
oTexto.Destroy;
oTexto := Nil;
end;
end;

Vamos pensar agora que nem sempre iremos ler o valor diretamente, como exemplo uma lista de objetos (não iremos implementar aqui, mas apenas como ajuda para interpretação OK?), como saberemos o tipo a ser tratado?

Simples, vamos mudar a implementação da classe!
Exemplo:

unit Model.ValorUnit;

interface

uses
System.TypInfo;

type
TValor = class
private
FValor: T;
FTipo: TTypeKind;
function GetValor: T;
procedure SetValor(const Value: T);
function GetTipo: TTypeKind;
public
procedure AfterConstruction; override;
property Valor: T read GetValor write SetValor;
property Tipo: TTypeKind read GetTipo;
end;

implementation

{ TValor }

procedure TValor.AfterConstruction;
var
Info: PTypeInfo;
begin
Info := System.TypeInfo(T);
try
if Info <> nil then
FTipo := Info^.Kind;
finally
Info := nil;
end;
end;

function TValor.GetTipo: TTypeKind;
begin
inherited;
result := FTipo;
end;

function TValor.GetValor: T;
begin
result := FValor;
end;

procedure TValor.SetValor(const Value: T);
begin
FValor := Value;
end;

end.

Nesta segunda implementação (ou alteração) a classe Tvalor agora possui dois campos, Fvalor e Ftipo.
Fvalor irá receber o valor própriamente dito, seja ele string integer ou qualquer outro, enquanto que Ftipo ira receber o tipo de dados que esta sendo utilizado dentro da classe e por consequencia no campo Fvalor.
Para definir o campo Ftipo, utilizei o procedimento AfterConstruction, este procedimento herdado da classe Tobject é chamado após o último construtor da classe ser executado. Segue abaixo o texto do Help do Delphi para melhor entendimento:
“Responds after the last constructor has executed.
AfterConstruction is called automatically after the object's last constructor has executed. Do not call it explicitly in your applications.
The AfterConstruction method implemented in TObject does nothing. Override this method when creating a class that performs an action after the object is created. For example, TCustomForm overrides AfterConstruction to generate an OnCreate event.”.

Neste procedimento, a nossa classe com o auxílio da System.TypInfo busca o tipo repassado ao criar um objeto do tipo Tvalor retornando o TtypeKind.
Tipos de TtypeKind:
TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString, tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray, tkUString, tkClassRef, tkPointer, tkProcedure).

Porque não usar a RTTI do Delphi?
No caso de Classes Genéricas, a RTTI não consegue achar a classe com o tipo especificado através da RTTI, não podendo retornar a classe e não retornando a classe, não se pode definir o tipo de dado da propriedade.

Nenhum comentário:

Postar um comentário