Property/ru

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) suomi (fi) français (fr) русский (ru)

Назад к Зарезервированным словам.


Зарезервированное слово property является частью объектно-ориентированного программирования. В русской терминологии обозначается как свойство. Свойства позволяют разграничить доступ (чтение, запись и т.д.) к полям внутри класса.

Синтаксис

property PropertyName: <type> <specifiers> default/nodefault;
property PropertyName[ident: <type>; ...]: <type> <specifiers>; default;


Идентификатор

Название идентификатора PropertyName должно соответствовать правиламObject Pascal.

Тип данных

Тип данных <type> может использоваться любой, доступный для применения у полей класса. Так же, когда спецификаторами read и write выбрано поле класса, тип свойства должен соответствовать типу этого поля. Пример:

TMyClass = class
  private
    FNumber: Integer;
  public
    property Number: Integer read FNumber write FNumber;
end;

В том случае, когда чтение происходит через функцию, результат функции должен имет тот же тип что и у свойства. Пример:

TMyClass = class
  private
    function getNumber: Integer;
  public
    property Number: Integer read getNumber;
end;

Спецификаторы

необязательный
обязательный
anchor kf.png требует определённого поведения, указанного после значка.
ic fluent link 24 filled.png подразумевает совместное использование

Спецификаторы имеют свой определённый порядок объявления:

Свойство Спецификаторы ПорядокОбъявленя.png


Спецификаторы разделяются на обязательные и не обязательные. Обязательными являются read и write. Причём достаточно использовать один из них. К примеру следующий код:

TMyClass = class
  private
    FNumber: Integer;
  public
    property Number: Integer read FNumber;
end;

Является полностью корректным. В данном случае свойство Number может быть только прочитано, так как спецификатор write здесь отсутствует.


index <0..High(Integer)>
anchor kf.png read <function getPropertyName(Index: Integer): <type>>
anchor kf.png write <procedure setPropertyName(Index: Integer; const Value: <type>)>

Спецификатор index позволяет обращаться к множеству имён свойств, поддерживаемых единственным методом доступа. Пример:

{$mode objfpc}  
Type  
  TPoint = class
    private  
      FX,FY : Longint;  
      function GetCoord (Index : Integer): Longint;  
      procedure SetCoord (Index : Integer; Value : longint);  
    public  
      property X : Longint index 1 read GetCoord write SetCoord;  
      property Y : Longint index 2 read GetCoord write SetCoord;  
      property Coords[Index : Integer]:Longint read GetCoord;  
  end;  
 
Procedure TPoint.SetCoord (Index : Integer; Value : Longint);  
begin  
  Case Index of  
   1 : FX := Value;  
   2 : FY := Value;  
  end;  
end;  
 
Function TPoint.GetCoord (INdex : Integer) : Longint;  
begin  
  Case Index of  
   1 : Result := FX;  
   2 : Result := FY;  
  end;  
end;  
 
Var  
  P : TPoint;  
 
begin  
  P := TPoint.create;  
  P.X := 2;  
  P.Y := 3;  
  With P do  
    WriteLn ('X=',X,' Y=',Y);  
end.


read <function getPropertyName: <type>>
<FField>
<function getPropertyName(ident: <type>; ...): <type>>

Спецификатор доступа, определяющий метод для чтения (геттер). Может быть полем или методом объекта, являющимся функцией.

Пример свойства, когда чтение происходит напрямую у поля объекта:

TMyClass = class
  private
    FNumber: Integer;
  public
    property Number: Integer read FNumber;
end;

Пример свойства, когда чтение происходит через геттер-функцию:

{ TMyClass }

TMyClass = class
  private
    FNumber: Integer;
    function getNumber: Integer;
  public
    property Number: Integer read getNumber;
end;

{ TMyClass }

function TMyClass.getNumber: Integer;
begin
  Result := FNumber;
end;

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

unit Unit2;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMyClass }

  TMyClass = class
    private
      FMother, FFather, FChild: String;
      function getMember(const MemberName: String): String;
    public
      property Family[MemberName: String]: String read getMember;
  end;

implementation

  { TMyClass }

  function TMyClass.getMember(const MemberName: String): String;
  var processed_MemberName: String;
  begin
    Result := '';
    processed_MemberName := LowerCase(MemberName);

    case processed_MemberName of
      'mother': Result := FMother;
      'father': Result := FFather;
      'child': Result := FChild;
    end;
  end;

end.


write <procedure setPropertyName(const Value: <type>)>
<FField>
<procedure setPropertyName(ident: <type>; ...; const Value: <type>)>

Спецификатор доступа, определяющий метод для записи (сеттер). Может быть полем или методом объекта, являющимся процедурой.

Пример свойства, когда запись происходит напрямую у поля объекта:

TMyClass = class
  private
    FNumber: Integer;
  public
    property Number: Integer write FNumber;
end;

Пример свойства, когда запись происходит через сеттер-функцию:

{ TMyClass }

TMyClass = class
  private
    FNumber: Integer;
    procedure setNumber(const Value: Integer): Integer;
  public
    property Number: Integer write setNumber;
end;

{ TMyClass }

function TMyClass.setNumber(const Value: Integer): Integer;
begin
  FNumber := Value;
end;

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

unit Unit2;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMyClass }

  TMyClass = class
    private
      FMother, FFather, FChild: String;
      function getMember(const MemberName: String): String;
      procedure setMember(const MemberName: String; const Value: String);
    public
      property Family[MemberName: String]: String read getMember write setMember;
  end;

implementation

  { TMyClass }

  function TMyClass.getMember(const MemberName: String): String;
  var processed_MemberName: String;
  begin
    Result := '';
    processed_MemberName := LowerCase(MemberName);

    case processed_MemberName of
      'mother': Result := FMother;
      'father': Result := FFather;
      'child': Result := FChild;
    end;
  end;

  procedure TMyClass.setMember(const MemberName: String; const Value: String);
  var processed_MemberName: String;
  begin
    processed_MemberName := LowerCase(MemberName);

    case processed_MemberName of
      'mother': FMother := Value;
      'father': FFather := Value;
      'child': FChild := Value;
    end;
  end;

end.


stored <True/False>
<FField: Boolean>
<function FunctionName: Boolean>
ic fluent link 24 filled.png default, nodefault

Спецификатор потоковой передачи, определяющий будет ли свойство хранится в файле формы. Может быть булевой константой, полем объекта или методом (функцией) без параметров с результатом в виде булевого значения. Отсутствие данного спецификатора эквивалентно True. Пример:

uses Math;

TMyClass = class
  private
    FNumberInt: Integer;
    FNumberReal: Real;
    FNumberReal_2: Real;
    FMyString: String;
    IsStored: Boolean;
    function isStoredNumberReal_2: Boolean;
  public
    property NumberInt: Integer read FNumberInt write FNumberInt stored False;
    property NumberReal: Real read FNumberReal write FNumberReal stored IsStored;
    property NumberReal: Real read FNumberReal write FNumberReal stored IsStored;
    property MyString: String read FMyString write FMyString; // эквивалентно True
end;                            

TMyClass.isStoredNumberReal_2: Boolean;
begin
  if CompareValue(FNumberReal_2, 3.14) <> 0 then
    Result := True
  else
    Result := False;
end;

Данный спецификатор связан с спецификатором default и nodefault. Когда значение свойства равно значению, указанному после default, данное свойство не будет сохранено в файле формы.


implements <IInterface>

Спецификатор для делегирования реализации интерфейсов агрегируемому объекту (входящий в состав вашего объекта и являющийся его полем). Пример:

IRadio = interface
  procedure Increment;
  procedure Decrement;
end;

IRecorder = interface
  procedure StartRecord;
  procedure Play;
end;

TRadio = class(TInterfacedObject, IRadio)
private
  FVolume : Integer;
  procedure SetVolume(const Value: Integer);
public
  procedure Increment;
  procedure Decrement;
  property Volume: Integer read FVolume write SetVolume;
  Destructor Destroy; override;
end;

TRadioRecorder = class(TInterfacedObject, IRadio, IRecorder)
private
  FRadio: TRadio;
  procedure SetRadio(const Value: TRadio):
public
  procedure StartRecord;
  procedure Play;
  constructor Create;
  property Radio: TRadio read FRadio write SetRadio implements IRadio;
end;
default <Порядковый тип>
<>
ic fluent link 24 filled.png stored

Данный спецификатор может применяться тремя способами. Первый способ это определения массива свойств объекта по умолчанию. Что позволит ссылаться на множество значений, используя одно и то же имя свойства. Например:

unit Unit2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMyClass }

  TMyClass = class
    private
      FNumbers: array [0..2] of Integer;
      function getNumber(const Index: Integer): Integer;
      procedure setNumber(const Index: Integer; const Value: Integer);
    public
      property Numbers[Index: Integer]: Integer read getNumber write setNumber; default;
  end;

implementation

  { TMyClass }

  function TMyClass.getNumber(const Index: Integer): Integer;
  begin
    Result := FNumbers[Index];
  end;

  procedure TMyClass.setNumber(const Index: Integer; const Value: Integer);
  begin
    FNumbers[Index] := Value;
  end;

end.

Вы можете не использовать массив в качестве места хранения значений, а определять их через функции, но само свойство должно оставаться свойством-массивом.

Так же стоит отметить несколько вещей. Во-первых, при использовании данного спецификатора с свойством-массивом, объявление default должно быть после всех спецификаторов через точку с запятой. Во-вторых данный способ не применим к простым типам, но есть возможность работы с внутренним объектом. В-третьих вы не можете определить два свойства-массива по умолчанию. Зато при наследовании вы можете его переназначить.

Второй способ связан с спецификатором stored. Данный способ работает только с порядковыми типами. В даннмо случае спецификатор default будет определять необходимость сохранения свойства в файл формы путём сравнения с конкретным значением. При создании экземпляра класса значение по умолчанию не применяется автоматически к свойству, ответственность за выполнение этого в конструкторе класса лежит на программисте. Пример:

unit Unit2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMyClass }

  TMyClass = class
    private
      FNumber: Integer;
    public
      constructor Create;
      property Number: Integer read FNumber write FNumber default 10;
  end;

implementation

  { TMyClass }

  constructor TMyClass.Create;
  begin
    FNumber := 10;
  end;

end.

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


nodefault <>
ic fluent link 24 filled.png stored

Спецификатор потоковой передачи. Может использоваться для обозначения, что свойство не имеет значения по умолчанию. Пр использовании с stored, обозначает, что свойство должно сохраняться в файл формы, если stored имеет значение True.

Документация