Delphi,记录类型属性,记录字段赋值:对预期记录的本地副本的赋值

问题描述 投票:8回答:1

在问题“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;

我很清楚为什么在没有记录中的setter的情况下,vcldeveloper的原始代码中会出现“左侧无法分配”错误。我也很清楚,如果为上面的代码中的属性Rec.A := 21;定义了一个setter,为什么没有为赋值TRec.A引发错误。

我不明白为什么赋值Rec.A := 21;将值21分配给FRec.FATForm1字段。我原以为这个值被分配给FA的本地临时副本的字段FRec,而不是FRec.FA本身。有谁可以请谈谈这里发生的事情?

delphi compiler-errors record
1个回答
8
投票

这是一个很好的问题!

您看到的行为是属性的实现细节的结果。编译器实现属性的方式因直接字段属性getter和函数属性getter而异。

当你写作

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
© www.soinside.com 2019 - 2024. All rights reserved.