Fenner
A linguagem

with de expressão e literal de record

R with (Campo: Valor) copia um record trocando só alguns campos; (Campo: Valor, …) cria um record onde se espera um.

O que é e por que existe#

Records são valores: LB := LA copia tudo. Mas "o mesmo record, só com a idade trocada" obrigava a copiar e depois atribuir campo a campo, em três linhas. Dois recursos pequenos resolvem isso e combinam com tipos imutáveis (readonly e init).

FormaO que faz
R with (Campo: Valor, …)cópia de R com só os campos citados trocados; R não muda
(Campo: Valor, …)literal de record, onde se espera um record; os campos não citados ficam zerados
Fenner
LAna := (Name: 'Ana', Age: 30, Address: (Street: 'Rua A', Number: 10));
LOlder := LAna with (Age: 31);              // LAna continua com 30
LMoved := LSeg with (Label_: 'movido', Target: (X: 0, Y: 0));

O with de expressão encadeia e aninha (Ender: (Rua: 'x') troca só a rua dentro do endereço), e conta as referências certo nos campos gerenciados (cadeias, por exemplo).

rc-with.fxp
program RecordsWith;
// Demo 29: records com métodos e o "with".
//
// Record é um tipo POR VALOR: atribuir copia os campos (diferente de classe,
// que copia só a referência). Pode ter construtor e métodos, sem custo de heap.
// Dois "with" diferentes existem:
//   R with (Campo: Valor)  -> expressão: cópia com alguns campos trocados
//   with Expr as A do ...  -> comando: apelido curto para Expr durante o bloco

type
  TPoint = record
    X, Y: Int32;
  public
    constructor Create(AX, AY: Int32);
    function Distance2: Int32;              // quadrado da distância à origem
    function Describe: UString;
  end record;

  TSegment = record
    Origin: TPoint;
    Target: TPoint;
    Label_: UString;
  end record;

constructor TPoint.Create(AX, AY: Int32);
begin
  X := AX;
  Y := AY;
end constructor;

function TPoint.Distance2: Int32;
begin
  Result := X * X + Y * Y;
end function;

function TPoint.Describe: UString;
begin
  Result := '(' + IntToUStr(X) + ', ' + IntToUStr(Y) + ')';
end function;

var
  LA, LB: TPoint;
  LSeg: TSegment;
  LMoved: TSegment;
begin
  LA := TPoint.Create(3, 4);
  LB := LA;                    // CÓPIA: LB é independente de LA
  LB.X := 10;
  WriteLn('LA = ', LA.Describe, '  LB = ', LB.Describe);
  WriteLn('distância² de LA: ', LA.Distance2);

  LSeg.Origin := LA;
  LSeg.Target := LB;
  LSeg.Label_ := 'original';

  // with de expressão: nova cópia, trocando só o que foi citado
  LMoved := LSeg with (Label_: 'movido', Target: (X: 0, Y: 0));
  WriteLn(LSeg.Label_, ': ', LSeg.Origin.Describe, ' -> ', LSeg.Target.Describe);
  WriteLn(LMoved.Label_, ': ', LMoved.Origin.Describe, ' -> ', LMoved.Target.Describe);

  // with de comando: evita repetir "LSeg.Origin."
  with LSeg.Origin as O do
  begin
    O.X := 100;
    WriteLn('alterado pelo apelido: ', O.Describe);
  end with;
  WriteLn('LSeg.Origin agora: ', LSeg.Origin.Describe);
end.
Saída
LA = (3, 4)  LB = (10, 4)
distância² de LA: 25
original: (3, 4) -> (10, 4)
movido: (3, 4) -> (0, 0)
alterado pelo apelido: (100, 4)
LSeg.Origin agora: (100, 4)

Demo 29. LMoved é cópia de LSeg com dois campos trocados; depois, o outro with (comando) altera o original por apelido.

Não confundir com o with de comando#

Existem dois with:

Fenner
LMoved := LSeg with (Label_: 'movido');     // EXPRESSÃO: devolve uma cópia
with LSeg.Origin as O do                    // COMANDO: um apelido curto para a expressão
  O.X := 100;                               // altera o ORIGINAL

O primeiro nunca altera o original. O segundo é só um apelido para não repetir LSeg.Origin.

Comparação e armadilhas#

  • O literal (Campo: Valor) precisa do tipo esperado (atribuição, argumento, Exit, campo aninhado). Os campos não citados valem zero/nil/vazio.
  • Num with de expressão, citar um campo que não existe é erro de nome, como em qualquer acesso.
  • Records com campos readonly só ganham o valor no construtor; o with de expressão cria uma cópia (atribuição de record inteiro é permitida).
  • Não confunda com tupla: (Name: 'Ana', Age: 30) onde se espera uma tupla com itens nomeados é outra coisa (Tuplas); o tipo esperado decide.

Veja também#