“Left side cannot be assigned to” for record type properties in Delphi

前端 未结 8 948
夕颜
夕颜 2020-12-01 16:09

I\'m curious to know why Delphi treats record type properties as read only:

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

  TForm1 = class(TForm)
         


        
相关标签:
8条回答
  • 2020-12-01 16:29

    The simplest approach is:

    procedure TForm1.DoSomething(ARec: TRec);
    begin
      with Rec do
        A := ARec.A;
    end;
    
    0 讨论(0)
  • 2020-12-01 16:34

    Yes this is a problem. But the problem can be solved using record properties:

    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;
    

    This compiles and workes without problem.

    0 讨论(0)
  • 2020-12-01 16:39

    The compiler is stopping you from assigning to a temporary. The equivalent in C# is permitted, but it has no effect; the return value of the Rec property is a copy of the underlying field, and assigning to the field on the copy is a nop.

    0 讨论(0)
  • 2020-12-01 16:40

    A solution I frequently use is to declare the property as a pointer to the record.

    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;
    

    With this, directly assigning Form1.Rec.A := MyInteger will work, but also Form1.Rec := MyRec will work by copying all the values in MyRec to the FRec field as expected.

    The only pitfall here is that when you wish to actually retrieve a copy of the record to work with, you will have to something like MyRec := Form1.Rec^

    0 讨论(0)
  • 2020-12-01 16:45

    Because you have implicit getter and setter functions and you cannot modify the Result of a function as it is a const parameter.

    (Note: In case you transform the record in an object, the result would actually be a pointer, thus equivalent to a var parameter).

    If you want to stay with a Record, you have to use an intermediate variable (or the Field variable) or use a WITH statement.

    See the different behaviors in the following code with the explicit getter and setter functions:

    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;
    
    0 讨论(0)
  • 2020-12-01 16:52

    This is because property are actually complied as a function. Properties only return or set a value. It is not a reference or a pointer to the record

    so :

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

    is same as calling a function like this:

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

    what you can do is:

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

    It is a bit messy but inherent in this type of architecture.

    0 讨论(0)
提交回复
热议问题