델파이의 레코드 유형 속성에 대해 "왼쪽에 할당 할 수 없습니다"
문제
델파이가 레코드 유형 속성을 읽는 이유를 알고 싶어합니다.
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;