239 lines
14 KiB
HTML
239 lines
14 KiB
HTML
<html>
|
||
|
||
<head>
|
||
<object type="application/x-oleobject" classid="clsid:1e2a7bd0-dab9-11d0-b93a-00c04fc99f9e">
|
||
<param name="Keyword" value="property">
|
||
<param name="Keyword" value="read">
|
||
<param name="Keyword" value="write">
|
||
<param name="Keyword" value="Свойства">
|
||
<param name="Keyword" value="Расширенные свойства">
|
||
<param name="Keyword" value="value">
|
||
<param name="Keyword" value="auto">
|
||
</object>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||
<title></title>
|
||
<link rel="StyleSheet" href="../../default.css">
|
||
</head>
|
||
|
||
<body>
|
||
<H1>Свойства</H1>
|
||
<p>Свойство внешне выглядит как поле
|
||
класса, однако, при доступе к нему на чтение или запись позволяет выполнять некоторые действия. Свойство описывается в классе
|
||
или записи следующим образом:</p>
|
||
<blockquote>
|
||
<p><code><b>property</b> Prop: <code class="codeit">тип</code> <b>read</b>
|
||
<code class="codeit">PropertyReader</code> <b>
|
||
write</b> <code class="codeit">PropertyWriter</code>;</code></p>
|
||
</blockquote>
|
||
<p>В качестве <code><code class="codeit">PropertyReader</code></code> может
|
||
выступать: </p>
|
||
<ul>
|
||
<li><em>имя функции чтения свойства</em>;</li>
|
||
<li><em>поле</em> соответствующего типа;</li>
|
||
<li><em>выражение</em> соответствующего типа (в этом случае свойство
|
||
называется <em>расширенным</em>). </li>
|
||
</ul>
|
||
<p>В качестве <code><code class="codeit">PropertyWriter</code></code> может
|
||
выступать: </p>
|
||
<ul>
|
||
<li><em>имя процедуры записи свойства</em>;</li>
|
||
<li><em>поле</em> соответствующего типа;</li>
|
||
<li><em>оператор</em> (в этом случае свойство называется <em>расширенным</em>).
|
||
В операторе доступна предопределённая переменная value, в которую помещается
|
||
значение для изменения свойства.</li>
|
||
</ul>
|
||
<p>Одна из секций - read или write - может быть опущена, в этом случае мы имеем
|
||
свойство с доступом только на запись или только на чтение соответственно.</p>
|
||
<p>При доступе к свойству на чтение вызывается <code><code class="codeit">
|
||
PropertyReader</code></code>, при доступе на запись - <code>
|
||
<code class="codeit">PropertyWriter</code></code>.</p>
|
||
<p>Как правило, каждое свойство связано с некоторым полем класса
|
||
и возвращает значение этого поля с помощью <i>функции чтения</i>, а меняет - с
|
||
помощью <i>процедуры записи</i>. Функция чтения и процедура записи должны быть
|
||
методами этого класса и иметь следующий вид:</p>
|
||
<blockquote>
|
||
<p><code><b>function</b> getProp: <code class="codeit">
|
||
тип</code>;<b><br>
|
||
procedure</b> setProp(value: <code class="codeit">тип</code>);</code> </p>
|
||
</blockquote>
|
||
<p>Если функция чтения свойства просто возвращает значение некоторого поля, то
|
||
вместо её имени можно указать имя этого поля. Аналогично если процедура записи
|
||
просто присваивает значение некоторому полю, то вместо её имени можно указать
|
||
имя этого поля.</p>
|
||
<p>Любая из секций <code><b>read</b></code>
|
||
или <code><b>write</b></code> может быть опущена, в этом
|
||
случае мы получаем свойство с доступом только на запись или с доступом только на чтение. </p>
|
||
<p>Обычно функция чтения и процедура записи описываются в
|
||
приватной секции класса. Они могут быть виртуальными, в этом случае их уместно
|
||
описывать в защищенной секции класса.</p>
|
||
<p>Для начала рассмотрим пример, в котором используются функция чтения и
|
||
процедура записи свойства:</p>
|
||
<blockquote>
|
||
<p><code><b>type</b> <br>
|
||
Person =<b> class<br>
|
||
private<br>
|
||
</b>fName: string;<br>
|
||
fAge: integer; <br>
|
||
<b> procedure</b> setAge(value: integer);<br> <b> begin</b><br>
|
||
<b> if</b> value<0 <b>then</b> <br>
|
||
value := 0;<br>
|
||
fAge := value <br><b> end</b>;<br>
|
||
<b>function</b> getAge: integer;<br>
|
||
<b>begin</b><br>
|
||
Result := fAge;<br>
|
||
<b>end</b>;<br>
|
||
<b>function</b> getName: string;<br>
|
||
<b>begin</b><br>
|
||
Result := fName;<br>
|
||
<b>end</b>;<br>
|
||
<b>function</b> getId: string;<br>
|
||
<b>begin</b><br>
|
||
Result := fName + fAge.ToString;<br>
|
||
<b>end</b>;<br>
|
||
<b> public<br>
|
||
constructor
|
||
</b>(name: string; age: integer);<br>
|
||
<b> begin<br>
|
||
</b> fName := name;<br>
|
||
fAge := age;<br>
|
||
<b> end</b>;<b><br>
|
||
</b> <b> property</b> Age: integer <b>read</b> getAge <b>write</b> setAge;<br> <b>property</b>
|
||
Name: string <b>read</b> getName;<br> <b> property</b> Id: string <b>read</b>
|
||
getId;<br><b> end</b>;</code></p>
|
||
<p><code><b>begin<br> var</b> p: Person;<br>
|
||
|
||
p := new Person('Иванов',20);<br> p.Age := -3; // p.Age = 0 !<br>
|
||
p.Age := p.Age + 1; // компилятор заменяет этот код на p.setAge(p.getAge
|
||
+ 1);<br>
|
||
|
||
writeln(p.Id); <br><strong>end</strong>.</code></p>
|
||
</blockquote>
|
||
<p>Всякий раз, когда мы присваиваем свойству <code>Age</code>
|
||
новое значение,
|
||
вызывается процедура <code>setAge</code> с
|
||
соответствующим параметром. Всякий раз, когда мы считываем значение свойства
|
||
<code>Age</code>, вызывается функция <code>getAge</code>. </p>
|
||
<p>Как уже отмечалось, в тривиальных случаях имя процедуры в секции <code> <b>write</b></code>
|
||
свойства и имя функции в секции <code> <b>read</b></code> свойства можно
|
||
заменить на имена соответствующих полей. Приведём код с учётом этого замечания:</p>
|
||
<blockquote>
|
||
<p><code><b>type</b> <br>
|
||
Person =<b> class<br>
|
||
private<br>
|
||
</b>fName: string;<br>
|
||
fAge: integer; <br>
|
||
<b> procedure</b> setAge(value: integer);<br> <b> begin</b><br>
|
||
<b> if</b> value<0 <b>then</b> <br>
|
||
value := 0;<br>
|
||
fAge := value <br><b> end</b>;<br>
|
||
<b>function</b> getId: string;<br>
|
||
<b>begin</b><br>
|
||
Result := fName + fAge.ToString;<br>
|
||
<b>end</b>;<br>
|
||
<b> public<br>
|
||
constructor
|
||
</b>(name: string; age: integer) := (fName,fAge) := (name,age);<br><b>
|
||
property</b> Age: integer <strong>read</strong> fAge <strong>write</strong>
|
||
setAge;<b><br> property</b> Name: string <strong>read</strong>
|
||
fName;<b><br> property</b> Id: string <strong>read
|
||
</strong>getId;<b><br> end</b>;</code></p>
|
||
</blockquote>
|
||
<p>Наконец, воспользуемся <em>расширенными свойствами</em>, заменив <code>getId</code>
|
||
на выражение <code>fName + fAge.ToString</code>, а <code>setAge</code> - на
|
||
оператор, реализующий тело этой процедуры:</p>
|
||
<blockquote>
|
||
<p><code><b>type</b> <br>
|
||
Person =<b> class<br>
|
||
private<br>
|
||
</b>fName: string;<br>
|
||
fAge: integer; <br>
|
||
<b> public<br>
|
||
constructor
|
||
</b>(name: string; age: integer) := (fName,fAge) := (name,age);<br>
|
||
<strong>property </strong>Age: integer <strong>read</strong> fAge <strong>
|
||
write</strong> fAge := value<0 ? 0 : value;<br> <strong> property</strong>
|
||
Name: string <strong>read</strong> fName;<br> <strong>
|
||
property</strong> Id: string <strong>read</strong> fName + fAge.ToString;<br>
|
||
<b> end</b>;</code></p>
|
||
</blockquote>
|
||
<p>Заметим, что расширенные свойства имеются только в PascalABC.NET, введены в
|
||
язык чтобы не писать отдельно функцию чтения или процедуру записи и отсутстуют в
|
||
других версиях Паскаля.</p>
|
||
<p>Расширенные свойства удобно использовать во многих ситуациях. Например, когда
|
||
обычные свойства рализованы обращением к аналогичным свойствам поля этого
|
||
класса:</p>
|
||
<blockquote>
|
||
<p><code><strong>type</strong><br> MyList<T> = <strong>class</strong><br>
|
||
<strong>private</strong> <br> l := new List<T>;<br>
|
||
<strong>public</strong> <br> <strong>property</strong>
|
||
Capacity: integer <strong>read</strong> l.Capacity <strong>write</strong>
|
||
l.Capacity := <em>value</em>;<br> <strong>end</strong>;</code></p>
|
||
<p><code><strong>begin</strong><br><b> var</b> ml := new
|
||
MyList<integer>;<br>
|
||
ml.Capacity := 5; // доступ на запись: значение 5 копируется в
|
||
переменную <em>value</em><br>
|
||
Println(ml.Capacity); // доступ на чтение<br><strong>end</strong>.</code></p>
|
||
</blockquote>
|
||
<p>Свойства не могут передаваться по ссылке в процедуры и функции. Например,
|
||
следующий код ошибочен:</p>
|
||
<blockquote>
|
||
<p><code>Inc(p.Age); // ошибка!</code></p>
|
||
</blockquote>
|
||
<p>Если требуется обработать значение свойства, передав его по
|
||
ссылке, то надо воспользоваться вспомогательной переменной:</p>
|
||
<blockquote>
|
||
<p><code>a := p.Age;<br>
|
||
Inc(a);<br>
|
||
p.Age := a;</code></p>
|
||
</blockquote>
|
||
<p>Однако, свойства соответствующих типов можно использовать в левой части
|
||
операций присваивания <code>+= -= *= /=</code>:</p>
|
||
<blockquote>
|
||
<p><code>p.Age += 1<span lang="en-us">;</span></code></p>
|
||
</blockquote>
|
||
<p>Свойства очень удобны при работе с визуальными объектами,
|
||
поскольку позволяют автоматически перерисовывать объект, если изменить
|
||
какие-либо его визуальные характеристики. Например, если создана
|
||
кнопка <code>b1</code> типа <code>
|
||
Button</code>,
|
||
то для визуального изменения ее ширины достаточно присвоить значение ее свойству
|
||
<code>Width</code>:</p>
|
||
<blockquote>
|
||
<p><code>b1.Width := 100;</code></p>
|
||
</blockquote>
|
||
<p>Процедура для записи этого свойства в приватное поле <code>fWidth</code> будет выглядеть примерно так:</p>
|
||
<blockquote>
|
||
<p><code><b>procedure</b> SetWidth(w: integer);<br>
|
||
<b>begin</b><br>
|
||
<b>if</b> (w>0) <b>and</b> (w<>fWidth) <b>then</b> <br>
|
||
<b>begin</b><br>
|
||
fWidth := w;<br>
|
||
</code> <i>код перерисовки кнопки</i><br>
|
||
<code> <b>end</b><br>
|
||
<b>end</b>;</code></p>
|
||
</blockquote>
|
||
<p>Следует обратить внимание на вторую часть условия в операторе
|
||
<code><b>if</b></code>: <code>w<>fWidth</code>.
|
||
Добавление этой проверки позволяет избежать лишней перерисовки кнопки в случае,
|
||
если ее ширина не меняется.</p>
|
||
<H1>Автоматически определяемые свойства</H1>
|
||
<p>В классе можно определить автосвойство, для которого автоматически
|
||
генерируется поле соответствующего типа. Автосвойство в PascalABC.NET доступны и
|
||
на чтение и на запись, а также могут иметь значение по умолчанию. Например:</p>
|
||
<blockquote>
|
||
<p><code><b>type</b> <br>
|
||
Position =<b> class<br>
|
||
public<br>
|
||
auto property </b>Row: integer;<br><b>
|
||
auto property</b> Col: integer;<br> <strong>end</strong>;</code></p>
|
||
</blockquote>
|
||
<p>или</p>
|
||
<blockquote>
|
||
<p><code><b>type</b> <br>
|
||
Grid =<b> class</b>(CommonElement)<b><br>
|
||
public<br>
|
||
auto property </b>Margin: integer := 5;<br> <strong>end</strong>;</code></p>
|
||
</blockquote>
|
||
</body>
|
||
|
||
</html> |