Fenner
A linguagem

Módulos e visibilidade

module e unit, public/private/protected, region, namespace e using, initialization e finalization.

module#

Um arquivo .fxm declara um módulo. Não há interface nem implementation: cada declaração diz se é public ou private, e a omissão é private.

Main.fxp
program Main;

uses Geometry;

begin
  WriteLn('lados: ', Geometry.Sides);
  WriteLn('área: ', SquareArea(5));
  WriteLn('id: ', NextId);
  WriteLn('id: ', NextId);
  // Square(3) aqui seria erro: é private no módulo
end.
Geometry.fxm
module Geometry;

// Um module não tem interface nem implementation: cada declaração diz se é
// public ou private, e a omissão é private.

public const
  Sides = 4;

private function Square(const X: Int32): Int32;
begin
  Exit(X * X);
end function;

public function SquareArea(const ASide: Int32): Int32;
begin
  Exit(Square(ASide));
end function;

// region agrupa e pode trocar a omissão de dentro dela
public region 'Counters';

  var
    Calls: Int32;

  function NextId: Int32;
  begin
    Inc(Calls);
    Exit(Calls);
  end function;

end region;

initialization
  Calls := 100;

end.
Saída
lados: 4
área: 25
id: 101
id: 102
Fenner
module Sales;

public type
  TCustomer = class(TObject) … end class;

private function Helper: Int32;       // só este módulo enxerga
begin … end function;

public function Total: Int32;         // todo mundo enxerga
begin … end function;

initialization
  …
finalization
  …
end.
  • O corpo begin…end. de um programa é a inicialização dele; o encerramento sai das finalization dos módulos, na ordem inversa da carga.
  • System e SysInit entram implicitamente em todo programa.
  • A forma unit clássica, com interface/implementation, continua aceita.
  • Recursão mútua sem interface: forward.
  • O corpo de um método não leva visibilidade; ela vem da classe.

Os três níveis#

VisibilidadeOutro módulo do projetoExportado no .fxd/.dll de um package
privatenãonão
protectedsimnão
publicsimsim

protected só faz diferença num package: é o que permite que dois módulos da mesma biblioteca se enxerguem sem publicar o que não é parte do contrato. Num program ou library não existe "fora", e protected equivale a public.

region#

region agrupa declarações e pode trocar a omissão de dentro dela. O rótulo em cadeia é para a IDE; o compilador o ignora. Fecha-se com end; ou end region;.

Fenner
public region 'Counters';        // dentro dela, a omissão passa a ser public
  var Calls: Int32;
  function NextId: Int32;
  begin … end function;
end region;

private region;                  // e pode voltar a fechar
  var Hidden: Int32;
end region;

Uma região aninhada herda a omissão da de fora.

rad_region#

rad_region 'rótulo'; … end rad_region; é a região que uma ferramenta escreve e reescreve; o designer do RAD a usa para os campos e os eventos de um formulário. É puramente léxica: não abre escopo nem visibilidade, e o conteúdo vale como se estivesse solto ali. Vale no corpo do módulo e dentro de uma classe. O rótulo é obrigatório (é por ele que a ferramenta acha a região) e o fecho exige o qualificador, para o fim não depender de contagem.

namespace e using#

namespace "Nome"; … end namespace; e using "Nome"; são só visibilidade: os nomes seguem únicos no módulo e nada muda no binário. Há três formas de alcançar um nome guardado num namespace:

  1. qualificando pela unit: UNs."Enderecos".TEndereco;
  2. abrindo com using: using "Enderecos"; e depois TEndereco;
  3. de dentro dele: quem está em "A"."B" enxerga o de "A".

O using vale no escopo em que foi escrito: dentro de uma região, só ali; no módulo, no módulo inteiro. Há formas curtas:

Fenner
procedure "System.Az".Teste;            // declara direto no namespace, sem abrir bloco
begin … end procedure;

using "System.Az" do                    // o using vale só para o próximo item
procedure UsaUmaVez;
begin
  WriteLn(Dobro(21));
end;

"System.Az".Teste();                    // citar qualificado, num comando

Um nome entre aspas duplas é um nome de namespace, não uma cadeia.

uses#

Fenner
uses System.Remainder, System.WCL, Unit1;

uses traz módulos pelo nome, procurando ao lado do projeto e nas pastas de SourcePath. Os módulos da RTL têm nomes com pontos (System.DB.SqLite). Um nome pode ser qualificado pelo módulo (Geometry.Sides), o que resolve colisões.

initialization e finalization#

initialization roda quando o módulo é carregado; finalization roda no encerramento, na ordem inversa da carga. Num late module a initialization roda por último e a finalization, primeiro (veja Metaprogramação).

Num class constructor, o código roda uma vez no arranque, e é podado se o tipo morrer na eliminação de código não usado.