Зачем использовать свойство в классе? - PullRequest
6 голосов
/ 18 июня 2011

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

TSampleClass = class
  public
    SomeInfo: integer;
end;

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

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

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

Спасибо

Ответы [ 7 ]

8 голосов
/ 18 июня 2011

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

Если у вас есть визуальный элемент управления, вам может потребоваться перекрасить элемент управления при изменении переменной/имущество.Например, предположим, что у вашего элемента управления есть 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и
  • элемент управления перекрашивается при его установке!
4 голосов
/ 18 июня 2011

Существуют реальные преимущества:

  • Свойства могут быть изменены, чтобы их можно было легко читать / писать / читать и записывать, без необходимости использовать отдельные методы получения и установки по всему коду;
  • Свойства можно сделать общедоступными / опубликованными в дочерних классах, просто добавив одну строку в разделе инициализации;
  • Свойства более удобны, когда дело доходит до настройки полей, сравните "Label.Font.SetSize (14) "с" Label.Font.Size: = 14 ", вы можете выровнять": = "с помощью табуляции / пробелов, и код будет намного более читабельным;

РЕДАКТИРОВАТЬ: Еще одна вещь, о которой я думалСвойства заставляют ограничивать методы Get / Set только одним параметром, что хорошо для ООП.Сравните это с некоторыми сверхпроектированными функциями:

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
3 голосов
/ 18 июня 2011

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

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

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

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

Комбинируя вышесказанное, вы можете изменить поведение свойств для классов-потомков.

2 голосов
/ 19 июня 2011

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

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

1 голос
0 голосов
/ 18 июня 2011

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

Ваши операции чтения / записи для свойства не должны быть переменной, они могут быть функциями.И тогда вы можете управлять "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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...