"الجانب الأيسر لا يمكن تعيين ل" لخصائص نوع السجل في دلفي
سؤال
وأنا الغريب أن تعرف لماذا دلفي يعامل سجل منشأة من نوع للقراءة فقط:
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;
هل هناك أي تفسير لهذا السلوك؟
المحلول
ومنذ "تم" هو الملكية، ومترجم يعاملها بشكل مختلف قليلا لأنه يحتوي على تقييم لأول مرة "قراءة" من DECL الممتلكات. النظر في هذا، وهو ما يعادل المثال الخاص بك غويا:
...
property Rec: TRec read GetRec write FRec;
...
إذا نظرتم اليها مثل هذا، يمكنك أن ترى أن أول إشارة إلى "تم" (قبل النقطة '.')، أن ندعو 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;
وهذا يجمع وworkes دون مشكلة.
وحل أنا كثيرا ما تستخدم غير لإعلان الملكية كمؤشر إلى السجل.
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 هي نسخة من الحقل المصدر، وتعيين إلى الميدان على نسخة هي نوب.
ولأن لديك الضمنية وظائف جالبة واضعة ولا يمكنك تعديل النتيجة من وظيفة كما هو محدد const.
و. (ملاحظة: في حال كنت تحويل الرقم القياسي في كائن، والنتيجة ستكون في الواقع مؤشر، مما يعادل معلمة فار)
إذا كنت ترغب في البقاء مع سجل، لديك لاستخدام متغير متوسطة (أو المتغير الميدانية) أو استخدام مع بيان.
واطلع على سلوكيات مختلفة في التعليمة البرمجية التالية مع وظائف جالبة واضعة صريحة:
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;
ودلفي سوف dereference المؤشر PREC بالنسبة لك، لذلك مثل هذه الامور لا تزال تعمل:
Form1.Rec.A := 1234;
وليس هناك حاجة لجزء الكتابة من الممتلكات، إلا إذا كنت تريد مبادلة PREC العازلة التي نقاط FRec في. أنا حقا لا تشير إلى قيام مثل هذه المقايضة عبر خاصية على أي حال.
وهذا النهج هو أبسط:
procedure TForm1.DoSomething(ARec: TRec);
begin
with Rec do
A := ARec.A;
end;