Ian Branch 128 Posted November 5, 2022 Hi Team, I have only ever used a Getter/Setter construct once and that was just copying some code. It worked, but I didn't really understand what was going on and at the time urgency was priority. I now believe I have a use for Getters & Setters but don't understand what they are actually doing. Can some one point me to a plain English explanation of what Getters & Setters do and how they do it. Do I need both a Getter & a Setter for the functionality to work? Be gentle.. 😉 Regards & TIA, Ian Share this post Link to post
David Heffernan 2354 Posted November 5, 2022 Do you mean for properties? Share this post Link to post
Dalija Prasnikar 1405 Posted November 5, 2022 59 minutes ago, Ian Branch said: Can some one point me to a plain English explanation of what Getters & Setters do and how they do it. Getters and Setters are methods called when you get (read) or set (write) property. In Delphi you don't need to have getter and setter methods. Properties can be directly connected to a field. You use getters or setters when you want to run additional code besides directly reading or writing some field value. Following is basic property declaration without any getters or setters. When you access property, compiler just directly reads or writes from associated field. TFoo = class protected FValue: Integer; published property Value: Integer read FValue write FValue; end; Following is property declaration with setter method (you can have any combination you need and you don't need to declare both methods if you just need one of them) TFoo = class protected FValue: Integer; procedure SetValue(AValue: Integer); published property Value: Integer read FValue write SetValue; end; Because getters and setters are methods, they will be a bit slower than directly using a field. If your setter is just setting a field and does nothing more, then you don't really need it. Same goes for getter. If it just returns the field then it is not needed. procedure TFoo.SetValue(AValue: Integer); begin FValue := AValue; end; If for instance you don't want to allow setting Value to zero, you might have following code in a setter. procedure TFoo.SetValue(AValue: Integer); begin if AValue <> 0 then FValue := AValue; end; You can even raise an exception if you set property to invalid value or similar. The only time when you would need to use getters and setters that just read or write to a field, is when your property needs to be accessible through interface. Interfaces require that getters and setters are methods. 3 3 Share this post Link to post
Ian Branch 128 Posted November 6, 2022 Thanks Team, In my case it is passing a value to a form about to be created. Ian Share this post Link to post
David Heffernan 2354 Posted November 6, 2022 29 minutes ago, Ian Branch said: Thanks Team, In my case it is passing a value to a form about to be created. Ian That sounds more like an argument to the constructor Share this post Link to post
Ian Branch 128 Posted November 6, 2022 Hi Team, To put further context, this is what I have.. In the Calling Form.. procedure TAudioForm.btnEditTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self); TracksAddEditForm.sMode := 'Edit'; TracksAddEditForm.iAlbum := Albums.FieldByName('Index').AsInteger; TracksAddEditForm.sTrack := TracksView.FieldByName('Title').AsString; TracksAddEditForm.Show; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self); TracksAddEditForm.sMode := 'Insert'; TracksAddEditForm.iAlbum := Albums.FieldByName('Index').AsInteger; TracksAddEditForm.sTrack := TracksView.FieldByName('Title').AsString; TracksAddEditForm.Show; end; In the Created form.. private { Private declarations } FsMode: string; FiAlbum: integer; FsTrack: string; function GetsMode: string; procedure SetsMode(const value: string); function GetiAlbum: integer; procedure SetiAlbum(const value: integer); function GetsTrack: string; procedure SetsTrack(const value: string); public { Public declarations } property sMode: string read GetsMode write SetsMode; property iAlbum: integer read GetiAlbum write SetiAlbum; property sTrack: string read GetsTrack write SetsTrack; end; var TracksAddEditForm: TTracksAddEditForm; implementation {$R *.dfm} function TTracksAddEditForm.GetsMode: string; begin result := FsMode; end; procedure TTracksAddEditForm.SetsMode(const value: string); begin FsMode := value; end; function TTracksAddEditForm.GetiAlbum: integer; begin result := FiAlbum; end; procedure TTracksAddEditForm.SetiAlbum(const value: integer); begin FiAlbum := value; end; function TTracksAddEditForm.GetsTrack: string; begin result := FsTrack; end; procedure TTracksAddEditForm.SetsTrack(const value: string); begin FsTrack := value; end; Now, this all works fine, the relevant information is available in the created form and everything happens as it should/is desired. I created this by just emulating what I did once before in another App.. IIUC, the code in the created form, TracksAddEditForm, is simply making the properties available to the calling/creating form by making them properties of TracksAddEditForm. OK. So, do I need both Get & Set if the information is only going one way? Or is that the necessary structure. Is it the Get or the Set that is being 'populated' from the calling/creating form? To answer a concerned expressed - Speed is not an issue. I guess I should ask, is there a better way to do this? Regards & TIA, Ian Share this post Link to post
TonyB 3 Posted November 6, 2022 (edited) Apart from data validation as suggested above, you can use setters and getters for other purposes e.g procedure SetName(NewName: string); begin if NewName<>fName then begin fName := NewName; ProjectNotSaved := true; end; end; Edited November 6, 2022 by TonyB 2 Share this post Link to post
Dalija Prasnikar 1405 Posted November 6, 2022 1 hour ago, Ian Branch said: I guess I should ask, is there a better way to do this? Your code can be simplified in several ways. I will write each step separately, so you can more easily adapt your code if you need additional functionality beyond the code shown here. First, as I mentioned in previous post, you don't need to write getter and setter functions at all, if you just read or write field in those and noting more. First optimization would be removing all that redundant code. This is not just speed optimization, but also makes cleaner code that is easier to read and follow. This code is functionally completely equivalent to your declaration and you would create and initialize the form the same way you do now. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string read FsMode write FsMode; property iAlbum: integer read FiAlbum write FiAlbum; property sTrack: string read FsTrack write FsTrack; end; Your next question was whether you need to have both getters and setters. If you are just setting property, then you don't need getter. Similarly, if you have read only property that you will just read, then you don't need setter. So next optimization would be following. There is no difference in how you create and initialize your form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string write FsMode; property iAlbum: integer write FiAlbum; property sTrack: string write FsTrack; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self); TracksAddEditForm.sMode := 'Insert'; TracksAddEditForm.iAlbum := Albums.FieldByName('Index').AsInteger; TracksAddEditForm.sTrack := TracksView.FieldByName('Title').AsString; TracksAddEditForm.Show; end; But, the best way to initialize any class that you will construct through code (without bringing in dependency injection) would be declaring all required information as parameters to the constructor, Such code would also require different code when constructing form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public constructor Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); reintroduce; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self, 'Insert', Albums.FieldByName('Index').AsInteger, TracksView.FieldByName('Title').AsString); TracksAddEditForm.Show; end; The advantage of passing all required data as parameters during construction process is that you cannot accidentally forget to initialize some required field. If some fields are optional, then you can stick to initializing through properties, but using simplified examples before this last one. 6 Share this post Link to post
ConstantGardener 31 Posted November 6, 2022 29 minutes ago, Dalija Prasnikar said: The advantage of passing all required data as parameters during construction process is that you cannot accidentally forget to initialize some required field. If some fields are optional, then you can stick to initializing through properties, but using simplified examples before this last one. ...or you can use default parameters or even overloaded constructors with different parameters. 2 Share this post Link to post
Lars Fosdal 1793 Posted November 6, 2022 3 hours ago, ConstantGardener said: overloaded constructors ... appear to be nice at first glance, but they tend to be confusing. I'd recommend unique names for each constructor instead. 2 Share this post Link to post
Attila Kovacs 631 Posted November 6, 2022 are you really using "Show" ? 1 Share this post Link to post
ConstantGardener 31 Posted November 6, 2022 39 minutes ago, Lars Fosdal said: ... appear to be nice at first glance, but they tend to be confusing. I'd recommend unique names for each constructor instead. ...good point, especally when your Code Insight is broken. Share this post Link to post
David Heffernan 2354 Posted November 6, 2022 3 hours ago, Lars Fosdal said: appear to be nice at first glance, but they tend to be confusing Why is it confusing? Share this post Link to post
Ian Branch 128 Posted November 6, 2022 (edited) All, Thank you All for your contributions, in particular Dalija for your clear explanations. Appreciated. Response to some questions.. 1. Using Show? Only during development. It is now a ShowModal inside a Try-finally-end. 2. Empty parameters. Yes in either case one of the parameters may be empty. 3. Using a constructor. Never knowingly done so. I tried Dalija's constructor but it gives me an E@065 Unsatisfied forward or external declaration: 'TTracksAddEditForm.Create'. Not an issue, I don't believe I will go that route. Interesting result using Dalija's code with just the 'write'. I couldn't read the values in the created form. If I changed the write to read, I could read the values in the second form but not write to them in the calling form. Conclusion, I need both read and write & no Getters/Setters. 🙂 Regards and thanks All again. Another small gap filled in the vast emptiness of my knowledge. Ian Edited November 6, 2022 by Ian Branch Share this post Link to post
David Heffernan 2354 Posted November 6, 2022 12 minutes ago, Ian Branch said: Using a constructor. Never knowingly done so. I tried Dalija's constructor but it gives me an E@065 Unsatisfied forward or external declaration: 'TTracksAddEditForm.Create'. Not an issue, I don't believe I will go that route. Easy to fix and 100% this is what you should do Share this post Link to post
Ian Branch 128 Posted November 6, 2022 Hi David, 2 minutes ago, David Heffernan said: Easy to fix and 100% this is what you should do I'm sure it is when you know how. 🙂 I am open to education on this matter, just as I was/am on anything I haven't used. Ian Share this post Link to post
Alexander Elagin 143 Posted November 6, 2022 13 minutes ago, Ian Branch said: 3. Using a constructor. Never knowingly done so. I tried Dalija's constructor but it gives me an E@065 Unsatisfied forward or external declaration: 'TTracksAddEditForm.Create'. It just means that you have to write the missing constructor for the TTracksAddEditForm: constructor TTracksAddEditForm.Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); begin inherited Create(AOwner); sMode := AsMode; iAlbum := AiAlbum; sTrack := AsTrack; end; Share this post Link to post
Dalija Prasnikar 1405 Posted November 6, 2022 21 minutes ago, Ian Branch said: . Using a constructor. Never knowingly done so. I tried Dalija's constructor but it gives me an E@065 Unsatisfied forward or external declaration: 'TTracksAddEditForm.Create'. Not an issue, I don't believe I will go that route. Oops... I forgot to copy-paste that part of the code... constructor TTracksAddEditForm.Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); begin inherited Create(AOwner); FsMode := AsMode; FiAlbum := AiAlbum; FsTrack := AsTrack; end; 1 Share this post Link to post
Ian Branch 128 Posted November 6, 2022 Hi Dalija, Where does that go and why? Ian Share this post Link to post
Dalija Prasnikar 1405 Posted November 6, 2022 5 minutes ago, Ian Branch said: Where does that go and why? Implementation section where your TTracksAddEditForm class is declared. Just like with any other method that is declared in a class, its implementation must be included inside that unit implementation section. Share this post Link to post
Ian Branch 128 Posted November 6, 2022 Ok. I did that... private { Private declarations } FsMode: string; FiAlbum: integer; FsTrack: string; public { Public declarations } // property sMode: string read FsMode write FsMode; // property iAlbum: integer read FiAlbum write FiAlbum; // property sTrack: string read FsTrack write FsTrack; constructor TTracksAddEditForm.Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); protected procedure CreateParams(var Params: TCreateParams); override; end; var TracksAddEditForm: TTracksAddEditForm; implementation {$R *.dfm} constructor TTracksAddEditForm.Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); begin inherited Create(AOwner); FsMode := AsMode; FiAlbum := AiAlbum; FsTrack := AsTrack; end; But it still doesn't like it. 😞 Share this post Link to post
Ian Branch 128 Posted November 6, 2022 (edited) Hi Team, OK. Got it sorted. Thank you very much to all for your patience and education. To reflect on what David said.. 39 minutes ago, David Heffernan said: Easy to fix and 100% this is what you should do Why?? I mean, is this just fancy code for the sake of it?? No offence David. Ian Edited November 6, 2022 by Ian Branch Share this post Link to post
Attila Kovacs 631 Posted November 6, 2022 I would not bother with the constructor just add a method for setup and show: procedure TTracksAddEditForm.ShowForm(const AsMode: string; AiAlbum: integer; const AsTrack: string); begin FsMode := AsMode; FiAlbum := AiAlbum; // or set the controls directly FsTrack := AsTrack; Show; end; use it: TTracksAddEditForm.Create(Self).ShowForm('Insert', blah, buh); or for showmodal function TSomeValueReturningForm.ShowForm(var AVar: string): TModalResult; begin FVar := AVar; Result := ShowModal; AVar := FVar; end; use it: lStringVar := 'Initialvalue'; case TSomeValueReturningForm.Create(Self).ShowForm(lStringVar) of mrOk: DoSomething(lStringVar); else Whatever... end; the variable var TracksAddEditForm : TTracksAddEditForm; is only needed if the forms are created with Application.CreateForm() by the dpr itself. Otherwise it should be deleted immediately. 2 Share this post Link to post
Ian Branch 128 Posted November 6, 2022 2 minutes ago, Attila Kovacs said: is only needed if the forms are created with Application.CreateForm() by the dpr itself. Otherwise it should be deleted immediately. I generally do, just hadn't got around to it yet. 1 Share this post Link to post