Зачем использовать свойство в классе?

Мне просто интересно, почему я должен использовать свойство в классе вместо "нормальных" переменных (атрибутов класса?). Я имею в виду вот что:--2-->

TSampleClass = class
  public
    SomeInfo: integer;
end;

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

в чем большая разница? Я знаю, что могу определить методы getter и setter для получения или сохранения свойства соответственно, но это возможно даже без переменной, являющейся "свойством".

Я попытался найти, почему использовать его, но ничего полезного не придумал, поэтому я спрашиваю здесь.

спасибо вы

7 ответов


это просто очень простой пример конкретного случая, но все же это очень распространенный случай.

если у вас есть визуальный элемент управления, Вам может потребоваться перекрасить элемент управления при изменении переменной / свойства. Например, предположим, что ваш элемент управления имеет BackgroundColor переменная/свойство.

самый простой способ добавления такой переменной / свойства-позволить ей быть открытой переменной:

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

и TMyControl.Paint процедуры, вы рисуете фон с помощью значение BackgroundColor. Но это не поможет. Потому что, если вы измените BackgroundColor переменной экземпляра управления не перекрашивать сам. Вместо этого новый цвет фона не будет использоваться до следующего перерисовки элемента управления по какой-либо другой причине.

поэтому вы должны сделать это так:

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

здесь

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

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

и тогда программист, использующий элемент управления, должен использовать MyControl1.GetBackgroundColor получить цвет, и использовать MyControl1.SetBackgroundColor установить ее. Это неловко.

используя свойства, вы можете иметь лучшее из обоих миров. Действительно, если вы это сделаете

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;

затем

  • С точки зрения программиста, он может как читать, так и задавать цвет фона с помощью одного идентификатора MyControl1.BackgroundColor собственность, а
  • элемент управления перекрашивается, когда он устанавливает его!

преимущества реальной жизни:

  • свойства могут быть изменены для чтения / записи / read'n'write легко, без необходимости хлопот с отдельными геттеров и сеттеров по всему коду;
  • свойства могут быть опубликованы / опубликованы в дочерних классах, просто добавив одну строку в разделе инициализации;
  • свойства более дружелюбны, когда дело доходит до установки полей, сравните "метку".Шрифт.SetSize (14) "with" Label.Шрифт.Размер := 14", вы можете выровнять ":=" с вкладки / пробелы и код будут гораздо более читаемыми;

EDIT: еще одна вещь, о которой я думал, свойства заставляют вас ограничивать методы Get/Set только 1 параметром, что хорошо для ООП. Сравните это с некоторыми чрезмерно спроектированными функциями:

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

Я знаю, что могу определить методы getter и setter для получения или сохранения свойства соответственно, но это возможно даже без переменной, являющейся "свойством".

Ну, нет. Сеттеры и геттеры-это обычные методы, которые вызываются как таковые только после их использования в качестве элементов чтения и записи свойства. Отсутствие свойства означает отсутствие геттера или сеттера, даже если они названы таковыми. Кроме того, сеттеры и геттеры это обычно объявляется закрытым или защищенным. Поэтому возможность вызывать их при использовании общедоступного поля вместо использования общедоступного свойства потребует перемещения этих методов в общедоступный раздел.

кроме того, большая разница между полями и свойствами-это возможность публикации и, следовательно, может использоваться в инспекторе объектов. Поля (других типов, а затем класс или интерфейс) не могут быть объявлены как опубликованные.

свойства также могут иметь большое значение - или быть полезным - в наследство. Технически, вы не можете переопределить свойство, но вы можете имитировать переопределение несколькими способами. Некоторые примеры, где имя свойства может быть вызвано из TDescendant, каждый со своей целью:

1) абстракция:

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) защита (как упоминалось кром,):

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;

по combinination выше, вы могли бы изменить поведение свойств для потомка занятия.


Это просто хорошая практика программирования, чтобы изолировать самые "внутренности" вашего класса от внешнего мира. Кроме того, информация об опубликованных свойствах хранится в RTTI, сгенерированном для класса и может быть доступна по их имени, перечислению и т. д. Эта функция используется, например, при чтении формы из ее сериализованной формы ресурса.


одной из основных причин использования свойств (независимо от того, что это больше OO) является проверка ввода, например, если вам нужно ограничить возраст класса сотрудника, чтобы быть в допустимом диапазоне, таком как 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;


вы не можете отслеживать изменение переменной без свойства.

ваше чтение / запись для свойства не должны быть переменной, они могут быть функциями. И тогда вы можете управлять "onChange" свойства.

например

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;