Delphi, свойство типа записи, назначение поля записи: ожидается присвоение локальной копии записи - PullRequest
0 голосов
/ 05 марта 2019

По вопросу «Левая сторона не может быть назначена» для свойств типа записи в Delphi , есть ответ от Toon 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;, если для свойства TRec.A определен установщик, как в случае кода выше.

Что я не понимаю, так это почемуприсваивание Rec.A := 21; присваивает значение 21 полю FRec.FA из TForm1.Я ожидал бы, что значение присваивается полю FA локальной временной копии FRec, но не самому FRec.FA.Может ли кто-нибудь пролить свет на то, что здесь происходит?

1 Ответ

0 голосов
/ 05 марта 2019

Это отличный вопрос!

Поведение, которое вы видите, является следствием деталей реализации свойств.То, как компилятор реализует свойства, отличается для прямых методов получения свойства поля и для методов получения свойства функции.

Когда вы пишете

Rec.A := 21;

Компилятор видит Rec и знает, что это свойство.Поскольку метод получения является прямым методом получения поля, компилятор просто заменяет Rec на FRec и компилирует код в точности так, как если бы вы написали

FRec.A := 21;

. Затем компилятор обнаруживает свойство A и используетметод установки, и поэтому ваше назначение становится

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;

Мне кажется очевидным, что поведение такой программы не должно зависеть от того, является ли свойство 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...