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).
| Forma | O 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 |
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).
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.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:
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 ORIGINALO 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
withde expressão, citar um campo que não existe é erro de nome, como em qualquer acesso. - Records com campos
readonlysó ganham o valor no construtor; owithde 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#
- Records e interfaces.
- Demo
29-records-with; demo55-confortomostra owithjunto de outros recursos.