J. *_*ser 8 delphi compiler-errors record
在以上的问题“左边不能被分配到”在德尔福记录类型的属性,有一个答案椿Krijthe演示如何分配至创纪录的财产的字段可以由记录的声明中使用特性来完成。为便于参考,以下是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自己。任何人都可以对这里发生的事情有所了解吗?
这是一个很好的问题!
您看到的行为是属性的实现详细信息的结果。对于直接字段属性获取器和函数属性获取器,编译器实现属性的方式有所不同。
当你写
Rec.A := 21;
编译器看到Rec并知道它是一个属性。由于该getter是直接字段getter,因此编译器将完全替换Rec为FRec并编译代码,就像您编写的一样
FRec.A := 21;
然后,编译器会遇到该A属性并使用setter方法,因此您的赋值变为
FRec.SetA(21);
因此,您观察到的行为。
假设您有一个函数获取器,而不是直接字段获取器
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