Свойства.

В Delphi пожелание ООП "не обращаться из программы к полям, а действовать через методы" нашло отражение в новом для ООП принципе — свойствах объекта.

Свойство определяется:

· полем, некого типа,

· методом по чтению этого поля (функцией) того же типа,

· методом по записи в поле (процедурой) с параметром того же типа.

type

tMyObject=

class(tObject)

fMyField:tMyType;

function getMyField:tMyType;

procedure setMyField (NewValue:tMyType);

property MyProperty:tMyType

read getMyField

write setMyField;

end;

Имена методов, конечно, задаются пользователем произвольно, и вместо этих имен могли бы, к примеру, использоваться Method1 и Method2. Тела методов getMyField и setMyField как и для всех других методов, должны быть описаны в разделе реализации. При этом у функции getMyField не должно быть параметров, а у setMyField – один параметр, через который передается (по имени или по значению) устанавливаемый параметр. Методы по чтению и записи не нужно явно вызывать в программе. Если имеются переменные, описанные как

var aMyObject:tMyObject;

aValue,aVariable:tMyType;,

то можно писать

aMyObject.MyProperty:=aValue;

aVariable:=aMyObject.MyProperty;

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

type

tAnObject=

class(tObject)

fMyField:tSomeType;

function GetValue:tSomeType;

property MyProperty:tSomeType

read GetValue;

end;

Если метод по чтению или записи просто должен читать или записывать в некое поле, вместо имени соответствующего метода (или обоих) можно непосредственно писать имя поля:

tSomeType

read myField

write myField;

Заметим, что свойство и его методы по чтению и записи, а также соответствующее поле данных обычно располагают в разных областях видимости: свойство делают published или public, а методы и поле данных – private или protected (см. далее).

Свойству можно присвоить значение по умолчанию с помощью ключевого слова default:

property active:Boolean

read GetActive

write SetActive {тут нет ’;’ !!!}

default true;

Свойство может выглядеть как массив (т.е. быть векторным):

property MyArray[I:Integer]:tMyType

read GetMyValue

write SetMyValue;

При этом функция GetMyValue должна быть совместима по типу с tMyType и иметь единственный параметр с тем же типом и именем, что и индекс свойства (обычно — целый тип):

function GetMyValue(I:Integer):tMyType;

Метод для записи должен первым параметром иметь индекс, а вторым — переменную нужного типа (ее можно передавать как по имени, т.е. с var, так и по значению):

procedure SetMyValue(I:Integer;NewValue:tMyType);

Можно определить некое главное, основное векторное свойство класса как default, и не упоминать его при доступе к этому свойству объекта по индексу. Не путать со значением default для свойства!

Пример: свойство Strings, являющееся массивом строк, определенное как default свойство для класса tMyObject:

type

tMyObject=

class(tObject)

...

property Strings[I:Integer]:string

read GetS

write PutS; {!!!после PutS обязательно ";"}

default;

...

end;

var aMyObject:tMyObject;

begin

...

aMyObject.Strings[1]:='обычный способ';

...

aMyObject[2]:='сокращенная форма';

...

end.

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

В Delphi предусмотрен еще один способ передачи параметров в процедуру: передача константы по имени (пишется директива const вместо var перед именем переменной). Все происходит так же, как при директиве var, но при этом внутри процедуры не может быть изменено значение этой переменной (как при передаче по значению). На этапе компиляции отслеживаются ошибочные попытки изменить значения переменной путем присвоения или передачи параметра по имени в какую-либо процедуру.

Пример двумерного свойства:

property MyMatrix[Row,Col:Integer]:real

read GetElement

write SetElement;

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

function GetElement(Row,Col:Integer):real;

procedure SetElement(Row,Col:Integer; const NewValue:Real);

Если многомерное свойство определено как default для данного класса, то к нему можно обращаться по индексу через имя объекта аналогично случаю одномерного векторного свойства.