Jump to content
AlanScottAgain

Left side cannot be assigned to

Recommended Posts

Hi
I don't understand why I'm getting this message:

 

This a cut down version of my object.

 

TDrawingObject = class
  private
    FObjectType: TDrawingObjectType;
    FBoundsRect: TRectF;
    FTag: Integer;
  public
     constructor Create(AObjectType: TDrawingObjectType);
    destructor Destroy;

   property ObjectType: TDrawingObjectType read FObjectType write FObjectType;
    property BoundsRect: TRectF read FBoundsRect write FBoundsRect;
    property Tag: Integer read FTag write FTag default 0;
  end;


Then in procedure:

 

var
 TempDrawingObject: TDrawingObject;

begin

  TempDrawingObject:= TDrawingObject.Create;
  TempResult.BoundsRect.Top:= 20;

 TempResult.Tag:= 5;

end;


I can not assign 20 to the BoundsRect.top Property.

But I can assign to the Tag property.

 

Do I need to create a setter?

What have I missed.

Many thanks for your advice.

Alan

Share this post


Link to post
Posted (edited)

Your variable FBoundsRect is of type TRectF which is a record. Records are passed by value, not by reference. Meaning: When you access TempResult.BoundsRect, you are not acting on the original data, but a copy on it.

Yes, in theory, you can access and modify the temporary data, but the compiler does not let you as it makes no sense to do so. The compiler message is unfortunately, once again, not helpful.

 

One solution is storing it in a temporary local variable, and then assigning it back, after you have changed the bounds rect.

 

Another one is getting rid of the the properties altogether as they just add noise and hide the fact that you can just replace the complete record, but not just parts of it, as you desire.

 

program Project1;

uses System.SysUtils, System.Types;

type
	TMyObject = class(TObject)
		strict private var _PropField: TRectF;
		public property PropField: TRectF read _PropField write _PropField;
		public var VarField: TRectF;
	end;

var
	myObject: TMyObject;
begin
	myObject := TMyObject.Create();
	myObject.VarField.Left := 12.0;
	myObject.PropField.Left := 12.0; // E2064 LEft side cannot be assigned to
end.

 

Edited by Der schöne Günther

Share this post


Link to post
Posted (edited)

In my absolutely personal opinion, never bother with properties at all. There is no real use for them. They have a lot of other disadvantages - You can't pass them as var or out parameters, Code completion will not tell you if a property is writeable or not. Many other languages are perfectly fine with no properties at all. I can think of C# properties, but they are capable of so much more.

 

In Delphi, a getter/setter makes it immediately clear if a value can be read or written, makes it obvious that there could be side effects as it's not just a regular variable, and can be stored in a function pointer. Also, you can make the setter protected and only allow yourself and your subclasses or another interface to change the value. None of this is possible with properties. In my opinion, they just get in the way and require even more typing as Delphi is already incredibly verbose.

Edited by Der schöne Günther

Share this post


Link to post
8 minutes ago, Der schöne Günther said:

You can't pass them as var or out parameters

 

9 minutes ago, Der schöne Günther said:

In Delphi, a getter/setter makes it immediately clear if a value can be read or written

These two statements appear in conflict with each other. Advocacy for using getter/setter methods directly, flies in the face of criticism that properties can't be passed as var or out params. Neither can methods. 

Share this post


Link to post

That they cannot be passed as var or out was more a comparison to simple fields, I should have made that clearer. Meaning: If you don't need getter/setters, I fail to see why you would ever go with properties at all.

Share this post


Link to post
34 minutes ago, Der schöne Günther said:

Meaning: If you don't need getter/setters, I fail to see why you would ever go with properties at all.

Such a simple example: I need to store or retrieve the PrimaryKey value there. Why would Setter / Getter be in such a simple case?

Share this post


Link to post
1 hour ago, Der schöne Günther said:

That they cannot be passed as var or out was more a comparison to simple fields, I should have made that clearer. Meaning: If you don't need getter/setters, I fail to see why you would ever go with properties at all.

So instead of code like

obj.foo := obj.bar + 10;

you prefer

obj.setFoo(obj.getBar() + 10);

or perhaps

obj.setFoo(obj.getBar + 10);

(note that I personally am not keen on being able to call a function without the parens because it leads to ambiguity for type inference)

Share this post


Link to post
1 hour ago, Der schöne Günther said:

In my opinion, they just get in the way and require even more typing as Delphi is already incredibly verbose.

Yep, sure. Especially if you need to make some internal field accessible as-is

I had some light experience with QT that has no properties but only g/setters. That were really awful hours.

Share this post


Link to post
2 hours ago, Der schöne Günther said:

That they cannot be passed as var or out was more a comparison to simple fields, I should have made that clearer. Meaning: If you don't need getter/setters, I fail to see why you would ever go with properties at all.

You can change the visibility of properties inherited from an ancestor, you cannot do that with fields. The VCL itself makes heavy use of that.

Share this post


Link to post
3 hours ago, Der schöne Günther said:

In my absolutely personal opinion, never bother with properties at all. There is no real use for them.

... unless they are published and used via RTTI, e.g. in the Object Inspector or in some serialization code.

 

Apart from that I partly agree. Especially the value of readonly properties is nearly zero and for interfaces they are a pain in the lower back.

Share this post


Link to post
3 hours ago, Der schöne Günther said:

never bother with properties at all. There is no real use for them.

Bold statement, given the fact that they were one of the language features, if not the feature, that made Delphi possible in the first place.

 

3 hours ago, Der schöne Günther said:

You can't pass them as var or out parameters, Code completion will not tell you if a property is writeable or not.

If you need to pass them as var or out parameters then I would say that there's something wrong with your design but without a concrete example it's hard to say what.

 

Code completion: Ditto. If you need to modify a property, which isn't writable, then there's something wrong with your design or with the class design.

 

3 hours ago, Der schöne Günther said:

Many other languages are perfectly fine with no properties at all.

Many other languages doesn't have objects, interfaces, exception handling, etc. Pretty irrelevant.

Share this post


Link to post
12 minutes ago, dummzeuch said:

Especially the value of readonly properties is nearly zero

So use a function instead but I can't see how that would improve anything for you. A property better expresses the intent.

 

15 minutes ago, dummzeuch said:

for interfaces they are a pain in the lower back

For interfaces properties are purely syntactic sugar so you really don't have to use them if you prefer not to. You can just call the getters or setters directly.

How is that a pain?

Share this post


Link to post
2 hours ago, dummzeuch said:

Especially the value of readonly properties is nearly zero

TSomeClass
private
  FFoo: string;
public
  // with props
  property Foo: string read FFoo;

  // without props
  function Foo: string;
  ...
  function Foo: string;
  begin
    Result := FFoo;
  end;

 

Share this post


Link to post
29 minutes ago, Fr0sT.Brutal said:
3 hours ago, dummzeuch said:

Especially the value of readonly properties is nearly zero


TSomeClass
private
  FFoo: string;
public
  // with props
  property Foo: string read FFoo;

  // without props
  function Foo: string;
  ...
  function Foo: string;
  begin
    Result := FFoo;
  end;

 

OK, I should have explicitly said "readonly properties with a getter method", because that's what I meant.

Share this post


Link to post
2 hours ago, Anders Melander said:
3 hours ago, dummzeuch said:

and for interfaces they are a pain in the lower back.

For interfaces properties are purely syntactic sugar so you really don't have to use them if you prefer not to. You can just call the getters or setters directly.

How is that a pain?

Because once these properties exist in an interface, every class that implements the interface must also declare them.

 

For a class at least properties are usually public/published while their getter/setter methods are private/protected, so one could argue that using a property reduces clutter. For an interface both the property and the getter/setter methods are public, so that argument doesn't apply.

Share this post


Link to post
3 minutes ago, dummzeuch said:

Because once these properties exist in an interface, every class that implements the interface must also declare them.

Nope:

type
  IFoo = interface
    function GetBar: IBar;
    procedure SetBar(const Value: IBar);
    property Bar: IBar read GetBar write SetBar;
  end;

  TFoo = class(..., IFoo)
  private
    function GetBar: IBar;
    procedure SetBar(const Value: IBar);
    // Look ma, No property!
  end;
  
begin
  var Foo: IFoo := TFoo.Create;
  var Bar := Foo.Bar;
end;

 

8 minutes ago, dummzeuch said:

For an interface both the property and the getter/setter methods are public, so that argument doesn't apply.

Sure, but I don't see the problem; Don't use them if you don't like them.

You will still need the getters and setters so there's not much saved by omitting the property declaration and you lose the meta information that the property communicates.

 

I too would wish the the getters and setters could be hidden from the public API of the interface (like I wish that the private part could be hidden from class declarations) but I guess that we have their history (as COM interfaces) to thank for that. Since I need both COM and non-COM interfaces and a new breed of interfaces would just be a mess I can live with the annoyances the versatility brings with it.

Share this post


Link to post
Posted (edited)
2 hours ago, Anders Melander said:

Nope:


type
  IFoo = interface
    function GetBar: IBar;
    procedure SetBar(const Value: IBar);
    property Bar: IBar read GetBar write SetBar;
  end;

  TFoo = class(..., IFoo)
  private
    function GetBar: IBar;
    procedure SetBar(const Value: IBar);
    // Look ma, No property!
  end;
  
begin
  var Foo: IFoo := TFoo.Create;
  var Bar := Foo.Bar;
end;

 

Wow, learned something new today.

Quote
3 hours ago, dummzeuch said:

For a class at least properties are usually public/published while their getter/setter methods are private/protected, so one could argue that using a property reduces clutter. For an interface both the property and the getter/setter methods are public, so that argument doesn't apply.

Sure, but I don't see the problem; Don't use them if you don't like them.

I am not talking about interfaces that I design, but those I have to use (e.g. in the ToolsAPI or 3rd party libraries). Not much choice there.

 

My own interfaces don't get properties. They usually have a getter that's called like the property would be (so MyValue rather than GetMyValue) and a setter with the standard name (SetMyValue).

 

Edited by dummzeuch

Share this post


Link to post
Posted (edited)
On 6/20/2022 at 1:39 AM, AlanScottAgain said:

TempResult.BoundsRect.Top:= 20;

Other people have already explained why that doesn't work.  But, if you really want it to make it work, you could declare your property as returning a PRectF pointer instead, eg:

type
  TDrawingObject = class
  private
    ...
    FBoundsRect: TRectF;
    ...
    function GetBoundsRect: PRectF;
  public
    ...
    property BoundsRect: PRectF read GetBoundsRect;
    ...
  end;

function TDrawingObject.GetBoundsRect: PRectF;
begin
  Result := @FBoundsRect;
end;

 

Edited by Remy Lebeau

Share this post


Link to post
25 minutes ago, dummzeuch said:

I am not talking about interfaces that I design, but those I have to use (e.g. in the ToolsAPI or 3rd party libraries). Not much choice there.

True, if you have to implement an interface declared by someone else - but since you don't have to actually declare the property in your implementation there's zero difference from your perspective between an interface with and one without property declarations. Anyway, to each their own, free world and all. IMO both approaches are just fine.

Share this post


Link to post
28 minutes ago, Remy Lebeau said:

you could declare your property as returning a PRectF pointer instead

Some of the code I'm currently maintaining ironically has widespread use of dynamic arrays of very large records. Ironically because it was done to avoid the overhead of lists of objects in order to improve performance. Instead it now struggles with the overhead of dynamic array reallocation and records being copied back and forth.

I'm slowly refactoring it to use lists and objects but meanwhile I'm using the approach you're suggesting (minus the copy/paste bug 🙂), so this:

type
  THumongousRec = record
    Foo: integer;
    ...
  end;

  THugeArray = array of THumongousRec;

  TMyClass = class
  private
    FData: THugeArray;
  protected
    function GetData(Index: integer): THumongousRec;
    procedure SetData(Index: integer; const Value: THumongousRec);
  public
    property Data[Index: integer]: THumongousRec read GetData write SetData;
  end;

function TMyClass.GetData(Index: integer): THumongousRec;
begin
  Result := FData[Index];
end;

procedure TMyClassSetData(Index: integer; const Value: THumongousRec);
begin
  FData[Index] := Value;
end;

begin
  var Data := MyClass.Data[0];
  Data.Foo := 1;
  MyClass.Data[0] := Data;
end;

becomes this:

type
  THumongousRec = record
    Foo: integer;
    ...
  end;
  PHumongousRec = ^THumongousRec;

  THugeArray = array of THumongousRec;

  TMyClass = class
  private
    ...same as before...
  protected
    ...same as before...
    function GetPData(Index: integer): PHumongousRec;
  public
    ...same as before...
    property PData[Index: integer]: PHumongousRec read GetPData;
  end;

function TMyClass.GetPData(Index: integer): PHumongousRec;
begin
  Result := @FData[Index];
end;

begin
  MyClass.PData[0].Foo := 1;
end;

Of course this only works when the property setter doesn't have side effects.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×