Delphi,记录类型 属性,记录字段分配:分配给预期记录的本地副本
Delphi, record type property, record field assignment: Assignment to local copy of record expected
关于 Toon Krijthe 的问题 “Left side cannot be assigned to” for record type properties in Delphi, there is 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的原始代码中出现"Left side cannot be assigned to"错误而记录中没有setter。我也很清楚,如果像上面的代码一样为 属性 TRec.A
定义了 setter,那么为什么赋值 Rec.A := 21;
不会出现错误。
我不明白的是为什么赋值Rec.A := 21;
将值21赋值给TForm1
的字段FRec.FA
。我本以为该值被分配给 FRec
的 本地临时副本的字段 FA
而不是 FRec.FA
本身。任何人都可以阐明这里发生的事情吗?
这是一个很好的问题!
您看到的行为是属性实现细节的结果。编译器实现属性的方式对于直接字段 属性 getters 和函数 属性 getters.
是不同的
写的时候
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;
在我看来很明显,这样一个程序的行为不应该取决于 属性 getter 是直接字段 getter 还是函数 getter .这似乎是 属性 功能与增强记录功能之间交互的设计缺陷。
这是一个非常简洁地演示问题的完整程序:
{$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
关于 Toon Krijthe 的问题 “Left side cannot be assigned to” for record type properties in Delphi, there is 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的原始代码中出现"Left side cannot be assigned to"错误而记录中没有setter。我也很清楚,如果像上面的代码一样为 属性 TRec.A
定义了 setter,那么为什么赋值 Rec.A := 21;
不会出现错误。
我不明白的是为什么赋值Rec.A := 21;
将值21赋值给TForm1
的字段FRec.FA
。我本以为该值被分配给 FRec
的 本地临时副本的字段 FA
而不是 FRec.FA
本身。任何人都可以阐明这里发生的事情吗?
这是一个很好的问题!
您看到的行为是属性实现细节的结果。编译器实现属性的方式对于直接字段 属性 getters 和函数 属性 getters.
是不同的写的时候
Rec.A := 21;
编译器看到Rec
并知道它是一个属性。由于 getter 是直接字段 getter,编译器只需将 Rec
替换为 FRec
并编译代码,就好像您编写了
FRec.A := 21;
然后编译器遇到A
属性并使用setter方法,所以你的赋值变成
FRec.SetA(21);
因此您观察到的行为。
假设您有一个函数 getter
而不是直接字段 getterproperty 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;
在我看来很明显,这样一个程序的行为不应该取决于 属性 getter 是直接字段 getter 还是函数 getter .这似乎是 属性 功能与增强记录功能之间交互的设计缺陷。
这是一个非常简洁地演示问题的完整程序:
{$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