在问题“Left side cannot be assigned to” for record type properties in Delphi上,来自Toon Krijthe的an answer演示了如何通过在记录的声明中使用属性来完成对记录属性的字段的分配。为便于参考,下面是Toon Krijthe发布的代码段。
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;
我很清楚,为什么在vcldeveloper的原始代码中没有记录设置程序的情况下引发了“无法分配给左侧”错误。我也很清楚,如果像上面的代码一样,为属性Rec.A := 21;
定义了一个setter,为什么分配TRec.A
时不会出现错误。
我不明白的是为什么赋值Rec.A := 21;
将值21赋给FRec.FA
的字段TForm1
。我希望该值分配给 FA
的本地临时副本的字段FRec
,而不是FRec.FA
本身。任何人都可以对这里发生的事情有所了解吗?
答案 0 :(得分:8)
这是一个很好的问题!
您看到的行为是属性的实现详细信息的结果。对于直接字段属性获取器和函数属性获取器,编译器实现属性的方式有所不同。
写作时
Rec.A := 21;
编译器看到Rec
并知道它是一个属性。由于getter是直接字段的getter,因此编译器只需将Rec
替换为FRec
并完全像编写代码一样编译代码
FRec.A := 21;
然后,编译器遇到A
属性并使用setter方法,因此您的赋值变为
FRec.SetA(21);
因此,您观察到的行为。
假设您有一个函数getter而不是直接字段getter
property Rec: TRec read GetRec;
....
function TForm1.GetRec: TRec;
begin
Result := FRec;
end;
在这种情况下
的处理Rec.A := 21;
更改。相反,编译器声明一个隐式局部变量,并且代码的编译如下:
var
__local_rec: TRec;
....
__local_rec := GetRec;
__local_rec.A := 21;
在我看来,这样一个程序的行为不应该取决于属性获取器是直接字段获取器还是函数获取器。这似乎是属性功能和增强记录功能之间的交互中的设计缺陷。
这是一个完整的程序,可以非常简洁地演示该问题:
{$APPTYPE CONSOLE}
type
TRec = record
private
FA: Integer;
procedure SetA(const Value: integer);
public
property A: integer read FA write SetA;
end;
procedure TRec.SetA(const Value: integer);
begin
FA := Value;
end;
type
TMyClass = class
private
FRec: TRec;
function GetRec: TRec;
public
property RecDirect: TRec read FRec;
property RecFunction: TRec read GetRec;
end;
var
Obj: TMyClass;
function TMyClass.GetRec: TRec;
begin
Result := FRec;
end;
begin
Obj := TMyClass.Create;
Obj.RecDirect.A := 21;
Writeln(Obj.FRec.FA);
Obj := TMyClass.Create;
Obj.RecFunction.A := 21;
Writeln(Obj.FRec.FA);
end.
输出
21 0