O que são e quais os tipos de Type Aliases #AQuemPossaInteressar

Categoria: Artigos
Categoria Pai: Addicted 2 Delphi!
Acessos: 1001
Imagem meramente ilustrativa

Type aliases (apelidos de tipos) são novos nomes para tipos que já existem e este é apenas um dos usos da palavra-chave type no Delphi. Neste rápido artigo vou falar apenas a respeito do uso desta palavra-chave para fornecer apelidos para tipos ou para criar novos tipos por derivação. Para todos os outros usos desta palavra-chave, clique neste link.

Existem duas formas de se definir um apelido para um tipo no Delphi, a primeira delas cria um apelido verdadeiro, ou seja, um simples pseudônimo para um outro tipo. Variáveis deste tipo podem ser utilizadas em qualquer contexto onde variáveis do tipo original poderiam ser usadas.

A segunda forma de definir uma apelido, cria na verdade um novo tipo baseado no tipo original, ou seja, cria uma derivação. Variáveis deste tipo não podem ser utilizadas (diretamente) nos mesmos contextos onde variáveis do tipo original poderiam ser usadas.

type
  TInfo = record
    Login: String;
    Senha: String;
  end;

  TApelidoParaTInfo = TInfo;
  TTipoDerivadoDeTInfo = type TInfo;

No código acima existe a declaração de um record TInfo, que seria o nosso tipo original. Apelidos podem ser criados para novos tipos e também para tipos que o Delphi provê, por exemplo, Integer, String, Float, Double, etc. Aqui, optei por criar um record apenas para deixar claro que isso pode ser feito.

Após a declaração de TInfo foram declarados dois apelidos, o primeiro é um apelido verdadeiro (TApelidoParaTInfo) e o segundo é um tipo derivado de TInfo (TTipoDerivadoDeTInfo), ou seja, ele é exatamente igual a TInfo, porém é incompatível com este por causa da derivação!

procedure EscreveInfo(AInfo: TInfo);
begin
  WriteLn(AInfo.Login,AInfo.Senha);
end;

procedure GeraInfo(out AInfo: TInfo);
begin
  AInfo.Login := 'abc';
  AInfo.Senha := '123';
end;

var
  TipoDerivadoDeTInfo: TTipoDerivadoDeTInfo;
  ApelidoParaTInfo: TApelidoParaTInfo;
  Info: TInfo;

O código acima possui dois procedures com um único parâmetro do tipo TInfo, no segundo caso este parâmetro é de saída. Também há a declaração de 3 variáveis, uma para cada tipo declarado.


O procedure GeraInfo, simplesmente inicializa uma variável do tipo TInfo passada em seu parâmetro e o procedure EscreveInfo, escreve numa janela de console o conteúdo de cada um dos campos de uma variável do tipo TInfo passada em seu parâmetro.

// Types of actual and formal var parameters must be identical
GeraInfo(TipoDerivadoDeTInfo);

// Sucesso!
GeraInfo(ApelidoParaTInfo);
GeraInfo(Info);

// Sucesso!
GeraInfo(TInfo(TipoDerivadoDeTInfo));
GeraInfo(TApelidoParaTInfo(TipoDerivadoDeTInfo));

// Incompatible types: 'TInfo' and 'TTipoDerivadoDeTInfo'
EscreveInfo(TipoDerivadoDeTInfo);

// Sucesso!
EscreveInfo(ApelidoParaTInfo);
EscreveInfo(Info);
EscreveInfo(TInfo(TipoDerivadoDeTInfo));
EscreveInfo(TApelidoParaTInfo(TipoDerivadoDeTInfo));

// Incompatible types: 'TTipoDerivadoDeTInfo' and 'TInfo'
TipoDerivadoDeTInfo := ApelidoParaTInfo;
TipoDerivadoDeTInfo := Info;

// Sucesso!
TipoDerivadoDeTInfo := TTipoDerivadoDeTInfo(ApelidoParaTInfo);
TipoDerivadoDeTInfo := TTipoDerivadoDeTInfo(Info);

// Incompatible types: 'TInfo' and 'TTipoDerivadoDeTInfo'
ApelidoParaTInfo := TipoDerivadoDeTInfo;
Info := TipoDerivadoDeTInfo;

// Sucesso!
ApelidoParaTInfo := TApelidoParaTInfo(TipoDerivadoDeTInfo);
ApelidoParaTInfo := TInfo(TipoDerivadoDeTInfo);
Info := TApelidoParaTInfo(TipoDerivadoDeTInfo);
Info := TInfo(TipoDerivadoDeTInfo);

// Sucesso!
ApelidoParaTInfo := Info;
Info := ApelidoParaTInfo;

Ao observar o trecho de código acima, podemos tirar várias conclusões:

Após analisar as várias assertivas acima você pode estar se perguntando quando se deve usar cada uma destas formas de type aliases:


Tipos derivados e RTTI

Como foi dito anteriormente uma característica importante do tipo derivado é que ele possui RTTI exclusivo. Isso o torna diferente do tipo original, mais ainda mantém a compatibilidade indireta com este. No desenvolvimento de componentes, por exemplo, muitas vezes é necessário criar um editor de propriedade pra uma propriedade específica. O mecanismo usado pelo Delphi para invocar os editores de propriedade, leva em conta o tipo da propriedade. Se esta propriedade for do tipo String e eu precisar de um editor para ela, por exemplo, para gerar uma string formatada, eu simplesmente executo um duplo clique na propriedade no Object Inspector (ou clico no botão com reticências) para invocar seu editor. O editor é associado a propriedade e seu tipo (no caso String), porém isso não é muito adequado, porque torna o editor "compatível" com qualquer propriedade do mesmo tipo (no caso String). Para impedir que um editor de propriedade que edita uma propriedade do tipo String seja usado com qualquer outra propriedade do mesmo tipo, basta criar um tipo derivado de String e associar o editor de propriedade àquele tipo. Ao fazer isso, o editor de propriedade não poderá ser associado a nenhuma outra propriedade do tipo String, mesmo que ele manipule a mesma informação (uma string). Além disso, quando precisarmos associar uma propriedade String ao editor de propriedade, bastará mudar o tipo desta propriedade para o tipo derivado, assim, a propriedade tirará proveito do editor de propriedade imediatamente, sem quebrar os dados que ela já contenha, pois o tipo derivado continua sendo uma string!

Para provar que os tipos derivados e os apelidos têm RTTI distintos, basta executar as linhas a seguir:

WriteLn('TApelidoParaTInfo é um '
       ,PTypeInfo(TypeInfo(TApelidoParaTInfo))^.Name
       ,', que é um '
       ,GetEnumName(TypeInfo(TTypeKind), Byte(PTypeInfo(TypeInfo(TApelidoParaTInfo))^.Kind)));
WriteLn('TTipoDerivadoDeTInfo é um '
       ,PTypeInfo(TypeInfo(TTipoDerivadoDeTInfo))^.Name
       ,', que é um '
       ,GetEnumName(TypeInfo(TTypeKind), Byte(PTypeInfo(TypeInfo(TTipoDerivadoDeTInfo))^.Kind)));

Ao executar o código acima, a seguinte saída vai ser exibida na janela de console:


TApelidoParaTInfo é um TInfo, que é um tkRecord
TTipoDerivadoDeTInfo é um TTipoDerivadoDeTInfo, que é um tkRecord
 

Como se pode ver, a informação retornada pelo RTTI para TApelidoParaTInfo diz que ele é na verdade um TInfo. Isso o torna 100% compatível com TInfo e indistinguível deste. Por outro lado o RTTI para TTipoDerivadoDeTInfo indica que ele é ele mesmo, e é por isso que ele é diretamente incompatível com TInfo e pode ser usado para identificar propriedades em componentes.