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