Delphi: Class Entries

The following situation:

type
  TRec = record
    Member : Integer;
  end; 

  TMyClass = class
  private
    FRec : TRec;
  public
    property Rec : TRec read FRec write FRec;
  end;

The following actions are not performed (the left side cannot be assigned), which is normal, since it TRecis a value type:

MyClass.Rec.Member := 0;

In D2007, although the following DOES work:

with MyClass.Rec do
  Member := 0;

Unfortunately, it does not work in D2010 (and I assume that it does not work in D2009 either). First question: why? Was this changed intentionally? Or is it just a side effect of some other changes? Was the D2007 workaround a "mistake"?

Second question: what do you think of the next workaround? Is it safe to use?

with PRec (@MyClass.Rec)^ do
  Member := 0;

I am talking about existing code here, so the changes that need to be made to make it work should be minimal.

Thank!

+3
6

MyClass.Rec.Member := 0;

. , "" - - , (AFAICT) . , " ".

:

  • MyClass.Rec , MyClass.Rec.
  • Expose TMyClass.Rec.Member .
+6

, " ", :

PMyRec = ^TMyRec;
TMyRec = record
  MyNum : integer
end;

TMyObject = class( TObject )
PRIVATE
  FMyRec : TMyRec;
  function GetMyRec : PMyRec;
PUBLIC
  property MyRec : PMyRec << note the 'P'
    read GetMyRec;
end;

function TMyObject.GetMyRec : PMyRec; << note the 'P'
begin
  Result := @FMyRec;
end;

, Delphi , :

MyObject.MyRec.MyNum: = 123;

, , , WITH - ! Brian

+3

, , .
WITH, - D2009, , D2010 ( ). , SO :

property RecField: Integer read FRec.A write FRec.A;
+1

, .

! . FRec , , , .

, ( TRec), /?

"TRec" , , , ?

, , , , .

0

, , , . , , , . , Getter

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FPoint: TPoint;
    function GetPoint: TPoint;
    procedure SetPoint(const Value: TPoint);
    { Private declarations }
  public
    { Public declarations }
    property Point : TPoint read GetPoint write SetPoint;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  with Point do
  begin
    X := 10;
    showmessage(IntToStr(x)); // 10
  end;

  with Point do
    showmessage(IntToStr(x)); // 0

  showmessage(IntToStr(point.x)); // 0
end;

function TForm2.GetPoint: TPoint;
begin
  Result := FPoint;
end;

procedure TForm2.SetPoint(const Value: TPoint);
begin
  FPoint := Value;
end;

end.

, Delphi/Borland .

, - .

, , With - Point.X: = 10;

0

:

procedure SetValue(i: Integer; const Value: Integer);
begin
  i := Value;
end;
SetValue(MyClass.Rec.Member, 10);

(. Getter/Setter)

/ Edit: The ugliest hack (and probably the most unsafe) follows, but it was so funny that I had to post it:

type
  TRec = record
    Member : Integer;
    Member2 : Integer;
  end;

  TMyClass = class
  private
    FRec : TRec;
    function GetRecByPointer(Index: Integer): Integer;
    procedure SetRecByPointer(Index: Integer; const Value: Integer);
  public
    property Rec : TRec read FRec write FRec;
    property RecByPointer[Index: Integer] : Integer read GetRecByPointer write SetRecByPointer;
  end;

function TMyClass.GetRecByPointer(Index: Integer): Integer;
begin
  Result := PInteger(Integer(@FRec) + Index * sizeof(PInteger))^;
end;

procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
begin
  PInteger(Integer(@FRec) + Index * sizeof(PInteger))^ := Value;
end;

It is assumed that each member of the record is equal to (P) Integer size and AV will fail if not.

  MyClass.RecByPointer[0] := 10;  // Set Member
  MyClass.RecByPointer[1] := 11;  // Set Member2

You can even hardcode offsets as constants and access them directly with offset

const
  Member = 0;
  Member2 = Member + sizeof(Integer);  // use type of previous member

  MyClass.RecByPointer[Member] := 10;

    function TMyClass.GetRecByPointer(Index: Integer): Integer;
    begin
      Result := PInteger(Integer(@FRec) + Index)^;
    end;

    procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
    begin
      PInteger(Integer(@FRec) + Index)^ := Value;
    end;

MyClass.RecByPointer[Member1] := 20;
-2
source

Source: https://habr.com/ru/post/1725928/


All Articles