"الجانب الأيسر لا يمكن تعيين ل" لخصائص نوع السجل في دلفي

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;

هل هناك أي تفسير لهذا السلوك؟

هل كانت مفيدة؟

المحلول

ومنذ "تم" هو الملكية، ومترجم يعاملها بشكل مختلف قليلا لأنه يحتوي على تقييم لأول مرة "قراءة" من 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top