Post: Registros gerenciados personalizados chegando ao Delphi 10.4

Alessandro Medeiros

Este artigo é uma tradução do artigo: https://blog.marcocantu.com/blog/2020-may-custom-managed-records.html

O que é um registro gerenciado no Delphi?

Os registros no Delphi podem ter campos de qualquer tipo de dados. Quando um registro possui campos simples (não gerenciados), como valores numéricos ou outros valores enumerados, não há muito o que fazer para o compilador. Criar e descartar o registro consiste em alocar memória ou se livrar do local da memória. (Observe que, por padrão, o Delphi não inicializa zero os registros.)

Se um registro tiver um campo de um tipo gerenciado pelo compilador (como uma string ou uma interface), o compilador precisará injetar código extra para gerenciar a inicialização ou finalização. Uma sequência, por exemplo, é contada como referência. Quando o registro fica fora do escopo, a sequência dentro do registro precisa ter sua contagem de referências diminuída, o que pode levar à desalocação da memória da sequência. Portanto, quando você está usando um registro gerenciado em uma seção do código, o compilador adiciona automaticamente um bloco try-finally em torno desse código e garante que os dados sejam limpos mesmo no caso de uma exceção. Este é o caso há muito tempo. Em outras palavras, os registros gerenciados fizeram parte da linguagem Delphi.

Registros com inicializar e finalizar operadores

Agora na 10.4, o tipo de registro Delphi suporta inicialização e finalização personalizadas, além das operações padrão que o compilador faz para registros gerenciados. Você pode declarar um registro com código personalizado de inicialização e finalização, independentemente do tipo de dados de seus campos, e pode escrever esse código personalizado de inicialização e finalização. Isso é possível adicionando novos operadores específicos ao tipo de registro (você pode ter um sem o outro, se quiser).
Abaixo está um trecho de código simples:


type
TMyRecord = record
Value: Integer;
class operator Initialize (out Dest: TMyRecord);
class operator Finalize(var Dest: TMyRecord);
end;

Você precisa escrever o código para os dois métodos de classe, é claro, por exemplo, registrando sua execução ou inicializando o valor do registro – aqui também estamos registrando uma referência ao local da memória, para ver qual registro está executando cada operação individual:


class operator TMyRecord.Initialize (out Dest: TMyRecord);
begin
Dest.Value := 10;
Log('created' + IntToHex (Integer(Pointer(@Dest)))));
end;

class operator TMyRecord.Finalize(var Dest: TMyRecord);
begin
Log('destroyed' + IntToHex (Integer(Pointer(@Dest)))));
end;

A enorme diferença entre esse mecanismo de construção e o que estava anteriormente disponível para registros é a chamada automática. Se você escrever algo parecido com o código abaixo, poderá chamar o inicializador e o finalizador e terminar com um bloco de tentativa e finalmente gerado pelo compilador para sua instância de registro gerenciado.


procedure LocalVarTest;
var
my1: TMyRecord;
begin
Log (my1.Value.ToString);
end;

Com esse código, você obterá um log como:

created 0019F2A8
10
destroyed 0019F2A8

Outro cenário é o uso de variáveis ​​embutidas, como em:


begin
var t: TMyRecord;
Log(t.Value.ToString);

que fornece a mesma sequência no log.

O operador Atribuir

A atribuição: = copia facilmente todos os dados dos campos de registro. Embora esse seja um padrão razoável, quando você tiver campos de dados e inicialização personalizados, poderá alterar esse comportamento. É por isso que, para registros gerenciados personalizados, você também pode definir um operador de atribuição. O novo operador é chamado com a sintaxe: =, mas definido como Designar:


class operator Assign (var Dest: TMyRecord; const [ref] Src: TMyRecord);

A definição do operador deve seguir regras muito precisas, incluindo o primeiro parâmetro como parâmetro de referência e o segundo como const passado por referência. Se você não fizer isso, o compilador emitirá mensagens de erro como as seguintes:


[dcc32 Error] E2617 First parameter of Assign operator must be a var parameter of the container type
[dcc32 Hint] H2618 Second parameter of Assign operator must be a const[Ref] or var parameter of the container type

Há um caso de amostra chamando o operador Atribuir:

var
  my1, my2: TMyRecord;
begin
  my1.Value := 22;
  my2 := my1;

produz este log (no qual também adicionamos um número de sequência ao registro):


created 5 0019F2A0
created 6 0019F298
5 copied to 6
destroyed 6 0019F298
destroyed 50019F2A0

Observe que a sequência de destruição é revertida da sequência de construção.

Passando registros gerenciados como parâmetros

Os registros gerenciados também podem funcionar de maneira diferente dos registros regulares quando passados ​​como parâmetros ou retornados por uma função. Aqui estão várias rotinas mostrando os vários cenários:


procedure ParByValue (rec: TMyRecord);
procedure ParByConstValue (const rec: TMyRecord);
procedure ParByRef (var rec: TMyRecord);
procedure ParByConstRef (const [ref] rec: TMyRecord);
function ParReturned: TMyRecord;

Agora, sem examinar cada registro um por um, este é o resumo das informações:

  • ParByValue cria um novo registro e chama o operador de atribuição (se disponível) para copiar os dados, destruindo a cópia temporária ao sair do procedimento
  • ParByConstValue não faz cópia e nenhuma chamada
  • ParByRef não faz cópia, não chama
  • ParByConstRef não faz cópia, não chama
  • ParReturned cria um novo registro (via Initialize) e, ao retornar, chama o operador Assign, se a chamada for como a seguir, e exclui o registro temporário, uma vez atribuído novamente como em:  my1: = ParReturned;

Exceções e registros gerenciados

Quando uma exceção é gerada, os registros em geral são limpos mesmo quando nenhuma tentativa explícita, finalmente o bloco está presente, diferentemente dos objetos. Essa é uma diferença fundamental e fundamental para a real utilidade dos registros gerenciados.


procedure ExceptionTest;
begin
var a: TMRE;
var b: TMRE;
raise Exception.Create('Error Message');
end;

Dentro deste procedimento, há duas chamadas de construtor e duas chamadas de destruidor. Novamente, essa é uma diferença fundamental e um recurso importante dos registros gerenciados. Consulte a seção posterior sobre um ponteiro inteligente simples, com base em registros gerenciados.

Matrizes de registros gerenciados

Se você definir uma matriz estática de registros gerenciados, haverá uma chamada inicial do operador Initialize na declaração do ponto:


var
a1: array [1..5] of TMyRecord; // call here
begin
Log ('ArrOfRec');

Todos eles são destruídos quando ficam fora do escopo. Se você definir uma matriz dinâmica de registros gerenciados, o código de inicialização será chamado com a matriz dimensionada (com SetLength):


var
a2: array of TMyRecord;
begin
Log ('ArrOfDyn');
SetLength(a2, 5); // call here

Conclusão

Esta é apenas uma introdução relativamente curta de um ótimo novo recurso que o Embarcadero está adicionando à linguagem Delphi para a próxima versão 10.4. Os registros gerenciados funcionam para registros genéricos, por exemplo, e em muitos outros cenários não abordados aqui. E, embora esse seja o principal recurso do novo idioma, outros surgirão como o novo gerenciamento de memória unificada em todas as plataformas. Fique ligado!

A Certificação Especialista Orientada a Objetos dará a você a oportunidade de melhorar seu software, otimizar o seu tempo e te dar a possibilidade de atender melhor os seus clientes. Conhecer a fundo esse paradigma e utilizar todos os seus benefícios irá facilitar muito a sua vida quando houver necessidade por parte de um cliente de um update rápido ou resolver um problema.

CLIQUE AQUI PARA SABER MAIS SOBRE A CERTIFICAÇÃO ESPECIALISTA EM PROGRAMAÇÃO ORIENTADA A OBJETOS

 




Faça sua busca

CATEGORIAS

POSTS RECENTES

E caso você tem interesse de conhecer mais sobre Registros gerenciados personalizados chegando ao Delphi 10.4, acesse o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI
Você não terá só conteúdos relacionados ao Registros gerenciados personalizados chegando ao Delphi 10.4, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.
Gostou?
Compartilhe:

Embarque no foguete com milhares de devs para aprender desenvolvimento, evoluir de forma contínua e se manter relevante no mercado.

Dúvidas
Cadastre-se em nossa lista

Para ter acesso em primeira mão, a tudo que acontece na Academia do Código, basta se cadastrar em nossa lista

Grupo Thulio Bittencourt | Academia do Código

#FaçaPartedaHistória

Copyright © 2024 – Todos os direitos reservados