开发者

Why use property in a class?

I was just wondering about why should I use property in a class instead of "normal" variables (class attributes?). What I mean is this:

TSampleClass = class
  public
    SomeInfo: integer;
end;

TPropertyClass = class
  private
    fSomeInfo: integer;
  public
    property SomeInfo: integer read fSomeInfo write fSomeInfo;
end;

What is the big difference? I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".

I tried searching for why to use it, but nothing useful came up, so I'm asking here.

开发者_如何学JAVA

Thank you


This is just a very simple example of a specific case, but still, it is a very common case.

If you have a visual control, you might need to repaint the control when you change a variable/property. For instance, let's say your control has a BackgroundColor variable/property.

The simplest way of adding such a variable/property is to let it be a public variable:

TMyControl = class(TCustomControl)
public
  BackgroundColor: TColor;
...
end;

And in the TMyControl.Paint procedure, you paint the background using the value of the BackgroundColor. But this doesn't do it. Because if you change the BackgroundColor variable of an instance of the control, the control doesn't repaint itself. Instead, the new background colour will not be used until the next time the control redraws itself for some other reason.

So you have to do it like this:

TMyControl = class(TCustomControl)
private
  FBackgroundColor: TColor;
public
  function GetBackgroundColor: TColor;
  procedure SetBackgroundColor(NewColor: TColor);
...
end;

where

function TMyControl.GetBackgroundColor: TColor;
begin
  result := FBackgroundColor;
end;

procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
  if FBackgroundColor <> NewColor then
  begin
    FBackgroundColor := NewColor;
    Invalidate;
  end;
end;

and then the programmer using the control has to use MyControl1.GetBackgroundColor to obtain the colour, and to use MyControl1.SetBackgroundColor to set it. That's awkward.

Using properties, you can have the best of both worlds. Indeed, if you do

TMyControl = class(TCustomControl)
private
  FBackgroundColor: TColor;
  procedure SetBackgroundColor(NewColor: TColor);
published
  property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor;
end;

...

procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
  if FBackgroundColor <> NewColor then
  begin
    FBackgroundColor := NewColor;
    Invalidate;
  end;
end;

then

  • from the programmer's point of view, he can both read and set the background colour using a single identifier, the MyControl1.BackgroundColor property, and
  • the control is repainted when he sets it!


There are real-life advantages:

  • Properties can be changed to be read/write/read'n'write easily, without need to hassle with separate Getters and Setters all over the code;
  • Properties can be made public/published in child classes by just adding one line in initialization section;
  • Properties are more friendly when it comes to setting fields, compare "Label.Font.SetSize(14)" with "Label.Font.Size := 14", you can align ":=" with tabs/spaces and code will be much more readable;

EDIT: Another thing I thought of, properties force you to limit Get/Set methods to only 1 parameter, which is good for OOP. Compare that to some over-engineered functions:

GetItem(Index:integer; ForcedIndex:boolean=false):TItem //Forced index to get any value
GetItem(Index:integer; out Res:PItem):boolean //Result signals if out pointer is valid


I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".

Well, no. Setters and getters are just normal methods that are called as such only once they are used as the read and write members of a property. Not having a property means not having a getter or a setter, even if they are named as such. Furthermore; setters and getters are typically declared private or protected. So being able to call them when you use a public field instead of using a public property would require to move those methods to the public section.

Also, a big difference between fields and properties is the ability to be published and thus can be used in the object inspector. Fields (of other types then class or interface) can not be declared as published.

Properties can also be of great importance - or be usefull - in inheritance. Technically, you can't override a property, but you can mimic override in several ways. Some examples where property Name can be called from TDescendant, each with its own purpose:

1) Abstraction:

TBase = class(TObject)
protected
  function GetName: String; virtual; abstract;
  procedure SetName(const Value: String); virtual; abstract;
public
  property Name: String read GetName write SetName;
end;

TDescendant = class(TBase)
private
  FName: String;
protected
  function GetName: String; override;
  procedure SetName(const Value: String); override;
end;

2a) Protection (like Krom mentioned, ):

TBase = class(TObject)
private
  FName: String;
  function GetName: String;
  procedure SetName(const Value: String);
protected
  property Name: String read GetName write SetName;
end;

TDescendant = class(TBase)
public
  property Name;
end;

2b)

TBase = class(TObject)
private
  FName: String;
protected
  function GetName: String;
  procedure SetName(const Value: String);
end;

TDescendant = class(TBase)
public
  property Name: String read GetName write SetName;
end;

By combinination of the above, you could change the behaviour of properties for descendant classes.


It is just a good programming practice to isolate the very "innards" of your class from the outside world. In addition, information about published properties are stored into RTTI generated for the class and can be accessed by their name, enumerated etc. This feature is used for example when reading a form from its serialized resource form.


One of main reason of using properties (regardless of it's more OO) is the validation of the input, for example if you need to limit the age of an employee class to be in valid range like 18..40

  TEmp = class
  private
    FName: string;
    FAge: Integer;
    procedure SetAge(const Value: Integer);
    procedure SetName(const Value: string);
  published
    property Name:string read FName write SetName;
    property Age:Integer read FAge write SetAge;
  end;

.....

procedure TEmp.SetAge(const Value: Integer);
begin
  if not (Value in [18..40]) then
    raise Exception.Create('Age must be between 18 and 40')
  else
    FAge := Value;
end;


You cant monitor the change in a variable without a property.

your read/writes for property dont have to be a variable they can be functions. And then you can manage the "onChange" of a property.

eg

TmyChange = procedure(Sender: Tobject) of object;


private 
Fchange : TmyChange;

public
property SomeInfo: integer read getFoo write setFoo;
property onChange : TmyChange read Fchange write Fchange;

function getFoo : integer
begin
  return localFoo;
end;

function setFoo (value : integer)
begin
  // validate incoming value
   localFoo=value;
  if assigned(Fchange) then Fchange(self);
end;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜