pascalabcnet/PABCNetHelp/LangGuide/Classes/properties.html

239 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>&nbsp;</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>
&nbsp; Person =<b> class<br>
&nbsp; private<br>
&nbsp;&nbsp;&nbsp; </b>fName: string;<br>&nbsp;
&nbsp; fAge: integer; <br>
<b>&nbsp;&nbsp;&nbsp; procedure</b> setAge(value: integer);<br>&nbsp; <b>&nbsp; begin</b><br>&nbsp;&nbsp;&nbsp;
<b>&nbsp; if</b> value&lt;0 <b>then</b> <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value := 0;<br>&nbsp; &nbsp;&nbsp;&nbsp;
fAge := value&nbsp; <br><b>&nbsp;&nbsp;&nbsp; end</b>;<br>
&nbsp;&nbsp;&nbsp; <b>function</b> getAge: integer;<br>
&nbsp;&nbsp;&nbsp; <b>begin</b><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := fAge;<br>
&nbsp;&nbsp;&nbsp; <b>end</b>;<br>
&nbsp;&nbsp;&nbsp; <b>function</b> getName: string;<br>
&nbsp;&nbsp;&nbsp; <b>begin</b><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := fName;<br>
&nbsp;&nbsp;&nbsp; <b>end</b>;<br>
&nbsp;&nbsp;&nbsp; <b>function</b> getId: string;<br>
&nbsp;&nbsp;&nbsp; <b>begin</b><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := fName + fAge.ToString;<br>
&nbsp;&nbsp;&nbsp; <b>end</b>;<br>
<b>&nbsp; public<br>
&nbsp;&nbsp;&nbsp; constructor
</b>(name: string; age: integer);<br>
<b>&nbsp;&nbsp;&nbsp; begin<br>
</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fName := name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
fAge := age;<br>
<b>&nbsp;&nbsp;&nbsp; end</b>;<b><br>
</b>&nbsp; <b>&nbsp; property</b> Age: integer <b>read</b> getAge <b>write</b> setAge;<br>&nbsp; &nbsp; <b>property</b>
Name: string <b>read</b> getName;<br>&nbsp; <b>&nbsp; property</b> Id: string <b>read</b>
getId;<br><b>&nbsp; end</b>;</code></p>
<p><code><b>begin<br>&nbsp; var</b> p: Person;<br>
&nbsp;
p := new Person('Иванов',20);<br>&nbsp; p.Age := -3; // p.Age = 0 !<br>
&nbsp; p.Age := p.Age + 1; // компилятор заменяет этот код на p.setAge(p.getAge
+ 1);<br>
&nbsp;
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>
&nbsp; Person =<b> class<br>
&nbsp; private<br>
&nbsp;&nbsp;&nbsp; </b>fName: string;<br>&nbsp;
&nbsp; fAge: integer; <br>
<b>&nbsp;&nbsp;&nbsp; procedure</b> setAge(value: integer);<br>&nbsp; <b>&nbsp; begin</b><br>&nbsp;&nbsp;&nbsp;
<b>&nbsp; if</b> value&lt;0 <b>then</b> <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value := 0;<br>&nbsp; &nbsp;&nbsp;&nbsp;
fAge := value&nbsp; <br><b>&nbsp;&nbsp;&nbsp; end</b>;<br>
&nbsp;&nbsp;&nbsp; <b>function</b> getId: string;<br>
&nbsp;&nbsp;&nbsp; <b>begin</b><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := fName + fAge.ToString;<br>
&nbsp;&nbsp;&nbsp; <b>end</b>;<br>
<b>&nbsp; public<br>
&nbsp;&nbsp;&nbsp; constructor
</b>(name: string; age: integer) := (fName,fAge) := (name,age);<br><b>&nbsp;&nbsp;&nbsp;
property</b> Age: integer <strong>read</strong> fAge <strong>write</strong>
setAge;<b><br>&nbsp;&nbsp;&nbsp; property</b> Name: string <strong>read</strong>
fName;<b><br>&nbsp;&nbsp;&nbsp; property</b> Id: string <strong>read
</strong>getId;<b><br>&nbsp; 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>
&nbsp; Person =<b> class<br>
&nbsp; private<br>
&nbsp;&nbsp;&nbsp; </b>fName: string;<br>&nbsp;
&nbsp; fAge: integer; <br>
<b>&nbsp; public<br>
&nbsp;&nbsp;&nbsp; constructor
</b>(name: string; age: integer) := (fName,fAge) := (name,age);<br>&nbsp;&nbsp;&nbsp;
<strong>property </strong>Age: integer <strong>read</strong> fAge <strong>
write</strong> fAge := value&lt;0 ? 0 : value;<br>&nbsp;&nbsp; <strong>&nbsp;property</strong>
Name: string <strong>read</strong> fName;<br>&nbsp;&nbsp;&nbsp; <strong>
property</strong> Id: string <strong>read</strong> fName + fAge.ToString;<br>
<b>&nbsp; end</b>;</code></p>
</blockquote>
<p>Заметим, что расширенные свойства имеются только в PascalABC.NET, введены в
язык чтобы не писать отдельно функцию чтения или процедуру записи и отсутстуют в
других версиях Паскаля.</p>
<p>Расширенные свойства удобно использовать во многих ситуациях. Например, когда
обычные свойства рализованы обращением к аналогичным свойствам поля этого
класса:</p>
<blockquote>
<p><code><strong>type</strong><br>&nbsp; MyList&lt;T&gt; = <strong>class</strong><br>&nbsp;
<strong>private</strong> <br>&nbsp;&nbsp;&nbsp; l := new List&lt;T&gt;;<br>&nbsp;
<strong>public</strong> <br>&nbsp;&nbsp;&nbsp; <strong>property</strong>
Capacity: integer <strong>read</strong> l.Capacity <strong>write</strong>
l.Capacity := <em>value</em>;<br>&nbsp; <strong>end</strong>;</code></p>
<p><code><strong>begin</strong><br><b>&nbsp; var</b> ml := new
MyList&lt;integer&gt;;<br>
&nbsp; ml.Capacity := 5; // доступ на запись: значение 5 копируется в
переменную <em>value</em><br>
&nbsp; 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>
&nbsp; <b>if</b> (w&gt;0) <b>and</b> (w&lt;&gt;fWidth) <b>then</b> <br>
&nbsp; <b>begin</b><br>
&nbsp;&nbsp;&nbsp; fWidth := w;<br>
</code>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<i>код перерисовки кнопки</i><br>
<code>&nbsp; <b>end</b><br>
<b>end</b>;</code></p>
</blockquote>
<p>Следует обратить внимание на вторую часть условия в операторе
<code><b>if</b></code>: <code>w&lt;&gt;fWidth</code>.
Добавление этой проверки позволяет избежать лишней перерисовки кнопки в случае,
если ее ширина не меняется.</p>
<H1>Автоматически определяемые свойства</H1>
<p>В классе можно определить автосвойство, для которого автоматически
генерируется поле соответствующего типа. Автосвойство в PascalABC.NET доступны и
на чтение и на запись, а также могут иметь значение по умолчанию. Например:</p>
<blockquote>
<p><code><b>type</b> <br>
&nbsp; Position =<b> class<br>
&nbsp; public<br>
&nbsp;&nbsp;&nbsp; auto property </b>Row: integer;<br><b>&nbsp;&nbsp;&nbsp;
auto property</b> Col: integer;<br>&nbsp; <strong>end</strong>;</code></p>
</blockquote>
<p>или</p>
<blockquote>
<p><code><b>type</b> <br>
&nbsp; Grid =<b> class</b>(CommonElement)<b><br>
&nbsp; public<br>
&nbsp;&nbsp;&nbsp; auto property </b>Margin: integer := 5;<br>&nbsp; <strong>end</strong>;</code></p>
</blockquote>
</body>
</html>