我很想知道为什么Delphi将记录类型属性视为只读:
TRec = record A : integer; B : string; end; TForm1 = class(TForm) private FRec : TRec; public procedure DoSomething(ARec: TRec); property Rec : TRec read FRec write FRec; end;
如果我尝试为Rec属性的任何成员赋值,我将得到"左侧无法分配"错误:
procedure TForm1.DoSomething(ARec: TRec); begin Rec.A := ARec.A; end;
允许对底层字段执行相同操作:
procedure TForm1.DoSomething(ARec: TRec); begin FRec.A := ARec.A; end;
这种行为有什么解释吗?
由于"Rec"是一个属性,编译器对它的处理方式略有不同,因为它必须首先评估属性decl的"读取".考虑一下,这在语义上等同于您的示例:
... property Rec: TRec read GetRec write FRec; ...
如果你这样看,你可以看到第一个引用"Rec"(在点'.'之前),必须调用GetRec,它将创建Rec的临时本地副本.这些临时设计在设计上是"只读的".这就是你遇到的.
你可以在这里做的另一件事是将记录的各个字段作为包含类的属性分解:
... property RecField: Integer read FRec.A write FRec.A; ...
这将允许您通过属性直接分配给类实例中该嵌入记录的字段.
是的,这是一个问题.但是使用记录属性可以解决问题:
type TRec = record private FA : integer; FB : string; procedure SetA(const Value: Integer); procedure SetB(const Value: string); public property A: Integer read FA write SetA; property B: string read FB write SetB; end; procedure TRec.SetA(const Value: Integer); begin FA := Value; end; procedure TRec.SetB(const Value: string); begin FB := Value; end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private FRec : TRec; public property Rec : TRec read FRec write FRec; end; procedure TForm1.Button1Click(Sender: TObject); begin Rec.A := 21; Rec.B := 'Hi'; end;
这编译和工作没有问题.
我经常使用的解决方案是将属性声明为指向记录的指针.
type
PRec = ^TRec;
TRec = record
A : integer;
B : string;
end;
TForm1 = class(TForm)
private
FRec : TRec;
function GetRec: PRec;
procedure SetRec(Value: PRec);
public
property Rec : PRec read GetRec write SetRec;
end;
implementation
function TForm1.GetRec: PRec;
begin
Result := @FRec;
end;
procedure TForm1.SetRec(Value: PRec);
begin
FRec := Value^;
end;
这样,直接分配Form1.Rec.A := MyInteger
将起作用,但也Form1.Rec := MyRec
可以按预期将所有值复制MyRec
到FRec
字段中.
这里唯一的缺陷是,当你想要实际检索要使用的记录副本时,你将不得不这样做 MyRec := Form1.Rec^
编译器阻止您分配临时.允许使用C#中的等价物,但它没有效果; Rec属性的返回值是基础字段的副本,并且分配给副本上的字段是一个nop.