델파이의 레코드 유형 속성에 대해 "왼쪽에 할당 할 수 없습니다"

StackOverflow https://stackoverflow.com/questions/620506

  •  05-07-2019
  •  | 
  •  

문제

델파이가 레코드 유형 속성을 읽는 이유를 알고 싶어합니다.

  TRec = record
    A : integer;
    B : string;
  end;

  TForm1 = class(TForm)
  private
    FRec : TRec;
  public
    procedure DoSomething(ARec: TRec);
    property Rec : TRec read FRec write FRec;
  end;

REC 속성의 구성원에게 값을 할당하려고하면 "왼쪽"을 "오류로 할당 할 수 없습니다.

procedure TForm1.DoSomething(ARec: TRec);
begin
  Rec.A := ARec.A;
end;

기본 필드와 동일하게 수행하는 동안 :

procedure TForm1.DoSomething(ARec: TRec);
begin
  FRec.A := ARec.A;
end;

그 행동에 대한 설명이 있습니까?

도움이 되었습니까?

해결책

"Rec"는 속성이므로 컴파일러는 먼저 속성 Decl의 "읽기"를 평가해야하기 때문에 약간 다르게 취급합니다. 이를 고려하십시오. 예는 예와 동일합니다.

...
property Rec: TRec read GetRec write FRec;
...

이렇게 보면 "rec"에 대한 첫 번째 참조 (도트 앞에서 ')에 대한 첫 번째 참조는 getRec을 호출해야합니다. 이 임시는 설계 "읽기 전용"입니다. 이것이 당신이 달리는 것입니다.

여기서 할 수있는 또 다른 일은 포함 클래스의 속성으로 레코드의 개별 필드를 분해하는 것입니다.

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

이를 통해 속성을 통해 클래스 인스턴스에 내장 된 레코드의 필드에 직접 할당 할 수 있습니다.

다른 팁

예, 이것은 문제입니다. 그러나 레코드 속성을 사용하여 문제를 해결할 수 있습니다.

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;

이것은 문제없이 컴파일하고 작동합니다.

자주 사용하는 솔루션은 속성을 레코드에 대한 포인터로 선언하는 것입니다.

type
  PRec = ^TRec;
  TRec = record
    A : integer;
    B : string;
  end;

  TForm1 = class(TForm)
  private
    FRec : TRec;

    function GetRec: PRec;
    procedure SetRec(Value: PRec);
  public
    property Rec : PRec read GetRec write SetRec; 
  end;

implementation

function TForm1.GetRec: PRec;
begin
  Result := @FRec;
end;  

procedure TForm1.SetRec(Value: PRec);
begin
  FRec := Value^;
end;

이것으로 직접 할당 Form1.Rec.A := MyInteger 효과가 있지만 또한 작동합니다 Form1.Rec := MyRec 모든 값을 복사하여 작동합니다 MyRec ~로 FRec 예상대로 필드.

여기서 유일한 함정은 실제로 작업 할 레코드 사본을 검색하고 싶을 때 MyRec := Form1.Rec^

컴파일러가 임시에 할당하는 것을 막고 있습니다. C#의 동등한 것은 허용되지만 효과는 없습니다. REC 속성의 반환 값은 기본 필드의 사본이며 사본의 필드에 할당하는 것은 NOP입니다.

암시 적 getter 및 setter 함수가 있고 함수 결과를 const 매개 변수로 수정할 수 없기 때문입니다.

(참고 : 객체에서 레코드를 변환하는 경우 결과는 실제로 포인터가되어 Var 매개 변수와 동일합니다).

레코드를 유지하려면 중간 변수 (또는 필드 변수)를 사용하거나 문을 사용해야합니다.

명시 적 getter 및 setter 기능을 사용하여 다음 코드의 다른 동작을 참조하십시오.

type
  TRec = record
    A: Integer;
    B: string;
  end;

  TForm2 = class(TForm)
  private
    FRec : TRec;
    FRec2: TRec;
    procedure SetRec2(const Value: TRec);
    function GetRec2: TRec;
  public
    procedure DoSomething(ARec: TRec);
    property Rec: TRec read FRec write FRec;
    property Rec2: TRec  read GetRec2 write SetRec2;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TForm2 }

procedure TForm2.DoSomething(ARec: TRec);
var
  LocalRec: TRec;
begin
  // copy in a local variable
  LocalRec := Rec2;
  LocalRec.A := Arec.A; // works

  // try to modify the Result of a function (a const) => NOT ALLOWED
  Rec2.A := Arec.A; // compiler refused!

  with Rec do
    A := ARec.A; // works with original property and with!
end;

function TForm2.GetRec2: TRec;
begin
  Result:=FRec2;
end;

procedure TForm2.SetRec2(const Value: TRec);
begin
  FRec2 := Value;
end;

속성이 실제로 함수로 준수되기 때문입니다. 속성은 값만 반환하거나 설정합니다. 레코드에 대한 참조 또는 포인터가 아닙니다.

그래서 :

Testing.TestRecord.I := 10;  // error

다음과 같은 기능을 호출하는 것과 같습니다.

Testing.getTestRecord().I := 10;   //error (i think)

당신이 할 수있는 일은 다음과 같습니다.

r := Testing.TestRecord;    // read
r.I := 10;
Testing.TestRecord := r;    //write

이 유형의 아키텍처에는 약간 지저분하지만 내재되어 있습니다.

다른 사람들이 말했듯이, 읽기 속성은 레코드의 사본을 반환하므로 필드 할당은 TFORM1이 소유 한 사본에 작용하지 않습니다.

다른 옵션은 다음과 같습니다.

  TRec = record
    A : integer;
    B : string;
  end;
  PRec = ^TRec;

  TForm1 = class(TForm)
  private
    FRec : PRec;
  public
    constructor Create;
    destructor Destroy; override;

    procedure DoSomething(ARec: TRec);
    property Rec : PRec read FRec; 
  end;

constructor TForm1.Create;
begin
  inherited;
  FRec := AllocMem(sizeof(TRec));
end;

destructor TForm1.Destroy;
begin
  FreeMem(FRec);

  inherited;
end;

델파이는 당신을위한 선행 포인터를 피할 것이므로 이와 같은 것들이 여전히 작동합니다.

Form1.Rec.A := 1234; 

FREC가 가리키는 프리 버퍼를 교체하지 않는 한 속성의 글쓰기 부분이 필요하지 않습니다. 나는 어쨌든 속성을 통해 그러한 교환을 할 것을 제안하지 않을 것입니다.

가장 간단한 접근법은 다음과 같습니다.

procedure TForm1.DoSomething(ARec: TRec);
begin
  with Rec do
    A := ARec.A;
end;
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top