Apresentando as variáveis inline na linguagem Delphi

Qualidade: 

Estrela ativaEstrela ativaEstrela ativaEstrela ativaEstrela ativa
 

O texto a seguir é uma tradução (versão brasileira1) do original disponível no blog de Marco Cantù (Marco Tech Blog) em http://blog.marcocantu.com/blog/2018-october-inline-variables-delphi.html.


A linguagem Delphi, no Delphi 10.3, teve uma mudança bastante significativa em seu núcleo, permitindo muito mais flexibilidade no tocante a declaração de variáveis locais, seu escopo e tempo de vida. Esta é uma mudança que quebra um princípio fundamental da Linguagem Pascal original, mas oferece um grande número de vantagens, reduzindo a quantidade de código em muitos casos.

O bloco de variáveis clássico

Desde o Turbo Pascal 1.0 até agora, seguindo as regras clássicas da Linguagem Pascal, toda declaração de variáveis locais tinha de ser feita em um bloco de variáveis escrito antes do início de uma função, procedimento ou método:

procedure Test;
var
  I: Integer;
begin
  I := 22;
  ShowMessage (I.ToString);
end;

Declarando variáveis inline

A nova sintaxe de declaração de variáveis inline permite que você declare a variável diretamente em um bloco de código (permitindo também múltiplos símbolos como de costume):

procedure Test;
begin
  var I, J: Integer;
  I := 22;
  j := I + 20;
  ShowMessage (J.ToString);
end;

Apesar desta parecer uma diferença pequena, quando comparada com o uso do bloco de variáveis clássico, existem vários efeitos colaterais bons desta forma de declaração que fazem das variáveis inline um recurso realmente valioso.

Está gostando do que está lendo? Ajude nosso site visitando nossos patrocinadores. Obrigado! :)

Inicializando variáveis inline

Uma mudança significativa em relação ao modelo clássico de declaração de variáveis locais é que a declaração e a inicialização de uma variável inline podem ser feitas em uma instrução simples, tal como pode ser feito com variáveis globais. Isso deixa as coisas mais legíveis e simples, quando comparado ao método clássico, onde a inicialização de todas as variáveis era feita no início de uma função, procedimento ou método:

procedure Test;
begin
  var I: Integer := 22;
  ShowMessage (I.ToString);
end;

Ainda, se o valor de uma variável estiver disponível apenas posteriormente no bloco de código, ao invés de configurar um valor inicial qualquer (como 0 ou nil) para só depois atribuir o valor real, você pode atrasar a declaração da variável até que você possa calcular um bom valor inicial para ela:

procedure Test1;
begin
  var I: Integer := 22;
  var J: Integer := 22 + I;
  var K: Integer := I + J;
  ShowMessage (K.ToString);
end;

Em outras palavras, enquanto no passado todas as variáveis locais eram visíveis para todo o bloco de código da função, procedimento ou método, agora uma variável inline é visível apenas após a posição de sua declaração até o final deste bloco de código.

Escopo e tempo de vida de variáveis inline declaradas em blocos aninhados

A limitação de escopo é mais relevante para as variáveis inline, pois ele não se aplica a toda função, procedimento ou método, mas apenas ao bloco begin-end onde a variável inline aparece. Em outras palavras, o escopo de uma variável inline é limitado ao bloco onde ela foi declarada e ela não pode ser usada fora deste bloco. Não apenas o escopo é limitado ao bloco begin-end, mas também o tempo de vida da variável é limitado a este bloco. Uma variável de um tipo de dados gerenciado (como uma interface, string ou record gerenciado) será descartada ao final do bloco onde ela foi declarada, e não ao final da função, procedimento ou método.

procedure Test2;
begin
  var I: Integer := 22;
  if I > 10 then
  begin
    var J: Integer := 3;
    ShowMessage (J.ToString);
  end
  else
  begin
    var K: Integer := 3;
    ShowMessage (J.ToString); // ERRO DE COMPILAÇÃO: "Undeclared identifier: J"
  end;
  // J e K não são acessíveis aqui
end;

Como se pode observar no trecho de código anterior, uma variável declarada dentro de um bloco begin-end é visível apenas neste bloco específico e não após o fim dele (end). Após o final das instruções if, J e K não são mais visíveis.

Está gostando do que está lendo? Ajude nosso site visitando nossos patrocinadores. Obrigado! :)

Como foi mencionado anteriormente, o efeito do escopo não é limitado apenas a visibilidade. Uma variável gerenciada, como uma referência de interface ou record, será corretamente limpa no fim do bloco onde ela foi declarada, em vez de no final da função, procedimento ou método:

procedure Test99;
begin

  // algum código

  if (something) then
  begin
    var Intf: IInterface = GetInterface; // Intf.AddRef
    var MRec: TManagedRecord = GetMRecValue; // MRec.Create + MRec.Assign
    UseIntf(Intf);
    UseMRec(MRec);
  end; // Intf.Release e MRec.Destroy serão implicitamente chamados ao final do escopo

  // mais código

end; // nenhuma limpeza adicional!

Inferência (dedução) de tipos para variáveis inline

Outro enorme benefício das variáveis inline é que o compilador agora pode, em várias circunstâncias, deduzir o tipo de uma variável inline, apenas observando o tipo da expressão ou valor atribuído a ela:

procedure Test;
begin
  var I := 22;
  ShowMessage (I.ToString);
end;

O tipo da expressão à direita do operador de atribuição (:=) é analisado a fim de determinar o tipo da variável. Alguns dos tipos de dados são "expandidos" para um tipo maior, como no caso anterior onde o valor numérico 22 (um ShortInt) é expandido para um Integer. Como regra geral, se a expressão à direita de := é um tipo inteiro e menor que 32 bits, a variável será declarada como um inteiro de 32 bits. Você pode declarar uma variável inline com um tipo de forma explícita, caso precise de uma variável de tamanho específico.

Embora esse recurso possa economizar a digitação de algumas teclas a mais para uma variável Integer ou String, a dedução de tipo de variável torna-se muito mais interessante no caso de tipos complexos, como instâncias de tipos genéricos. No trecho de código a seguir, os tipos deduzidos são TDictionary para a variável MyDictionary e TPair, para a variável APair:

procedure NewTest;
begin
  var MyDictionary := TDictionary<string, Integer>.Create
  MyDictionary.Add ('one', 1);
  var APair := MyDictionary.ExtractPair('one');
  ShowMessage (APair.Value.ToString)
end;

Constantes inline

Além das variáveis, você agora pode declarar uma constante de forma inline. Isso pode ser feito tanto para constantes tipadas como não tipadas e neste último caso, o tipo é deduzido (um recurso que já existia para constantes há muito tempo). A seguir um exemplo simples:

const M: Integer = (L + H) div 2; // identificador simples, com tipo explícito
const M = (L + H) div 2; // identificador simples, sem tipo explícito (será deduzido)

Loops "for" com declaração inline de variável de controle

Outra circunstância específica na qual você pode tirar vantagem das declarações de variáveis inline são o seu uso como variáveis de controle tanto em loops "for-to" clássicos como nos modernos loops "for-in":

for var I: Integer := 1 to 10 do ...
for var Item: TItemType in Collection do...

Você pode simplificar ainda mais o código tirando vantagem da dedução de tipos:

for var I := 1 to 10 do ...
for var Item in Collection do ...

Este é um caso no qual ter a variável inline com um escopo limitado é particularmente benéfico, como no exemplo a seguir: ao tentar usar a variável I fora do loop irá causar um erro de compilação (no passado apenas um aviso era emitido em alguns casos):

procedure ForTest;
begin
  var total := 0;
  for var I: Integer := 1 to 10 do
    Inc (Total, I);
  ShowMessage (total.ToString);
  ShowMessage (I.ToString); // ERRO DE COMPILAÇÃO: Undeclared Identifier "I"
end;

Conclusão

As declarações de variáveis inline com dedução de tipos e escopo local, trazem ar fresco para a linguagem Delphi. Embora a legibilidade do código-fonte Pascal continue sendo um princípio a ser preservado, foi importante modernizar a linguagem em seu núcleo, removendo um pouco da ferrugem (real ou percebida). No caso das variáveis inline, existem muitas vantagens ao sair do estilo de codificação tradicional, cujo valor é evidente.

Ainda assim você pode manter seu código como está e pedir a seus colegas desenvolvedores que sigam a declaração tradicional. Nada no código ou estilo existente está errado entretanto as declarações de variáveis inline oferecem novas oportunidades. Eu já tenho problemas para voltar para versões mais antigas do Delphi... Apenas dizendo.

Está gostando do que está lendo? Ajude nosso site visitando nossos patrocinadores. Obrigado! :)
1O texto não é uma tradução "um para um". Palavras foram substituídas, removidas ou adicionadas a fim de garantir o pleno entendimento do texto por leitores lusófonos
Ajude nosso site visitando nossos patrocinadores!

Temos 32 visitantes e Nenhum membro online nos últimos 10 minutos (3.2 visitantes por minuto).