Jump to content
Ian Branch

Getters & Settters??

Recommended Posts

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
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. 

  • Like 3
  • Thanks 3

Share this post


Link to post
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

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

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 by TonyB
  • Like 2

Share this post


Link to post
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.

 

  • Like 6

Share this post


Link to post
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.

  • Like 2

Share this post


Link to post
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.

 

  • Like 2

Share this post


Link to post
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. :classic_wink:

Share this post


Link to post

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 by Ian Branch

Share this post


Link to post
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

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
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
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;

 

 

  • Like 1

Share this post


Link to post
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

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. 😞

image.thumb.png.784bc85958d7fe332e714dfbea454230.png

Share this post


Link to post

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 by Ian Branch

Share this post


Link to post

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.

 

  • Like 2

Share this post


Link to post
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.

  • Like 1

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

×