tag:blogger.com,1999:blog-19200333945995825642024-03-08T18:49:28.445+03:00Заметки о Pascal, Delphi и LazarusСледует ожидать переводов разделов справочной системы Delphi, компиляций из учебников, переводы статей, "путевые заметки" и прочие интересности. Блог прежде всего ориентирован на студентов, но опытных людей я тоже буду рад видеть;-)svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.comBlogger48125tag:blogger.com,1999:blog-1920033394599582564.post-60139577684126703032013-08-30T03:10:00.001+04:002013-08-30T03:10:34.084+04:00Объекты автоматизации<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Automation_Objects_(Win32_Only)" target="_blank">Automation Objects (Win32 Only)</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><p>Объект, класс которого поддерживает интерфейс IDispatch (объявлен в модуле System), является объектом автоматизации (Automation object).</p><p>Для обращения к объектам автоматизации следует использовать переменные типа вариант. Когда переменная типа вариант ссылается на объект автоматизации, с ее помощью вы можете вызывать методы этого объекта, читать и записывать значения его свойств. Для того чтобы сделать это, подключите модуль ComObj в раздел uses вашей программы или библиотеки.</p><a name='more'></a><br><br />
<h3>Диспетчеризация интерфейсных типов</h3><p>Диспетчеризация интерфейсных типов определяет методы и свойства, которые объект автоматизации поддерживает через IDispatch. Вызовы методов интерфейса диспетчеризации при выполнении программы направляются через метод Invoke интерфейса IDispatch, так как класс не может поддерживать интерфейс диспетчеризации.</p><p>Объявление интерфейса диспетчеризации имеет вид:</p><pre class="brush:pas">type interfaceName = dispinterface
['{GUID}']
memberList
end;</pre><p>где [{GUID}] – опционален, а memberList состоит из объявлений свойств и методов. Объявления интерфейсов диспетчеризации схожи с объявлениями обычных интерфейсов, но в них не может быть указан интерфейс-предок. То есть:</p><pre class="brush:pas">type
IStringsDisp = dispinterface
['{EE05DFE2-5549-11D0-9EA9-0020AF3D82DA}']
property ControlDefault[Index: Integer]: OleVariant dispid 0; default;
function Count: Integer; dispid 1;
property Item[Index: Integer]: OleVariant dispid 2;
procedure Remove(Index: Integer); dispid 3;
procedure Clear; dispid 4;
function Add(Item: OleVariant): Integer; dispid 5;
function _NewEnum: IUnknown; dispid -4;
end;</pre><br><h3>Методы интерфейса диспетчеризации</h3><p>Методы интерфейса диспетчеризации являются прототипами метода Invoke в "подложенной" реализации IDispatch. Для того чтобы определить ID диспетчера автоматизации для метода, необходимо включить в его объявление dispid, следом за которым указывается целочисленная константа. При указании константы, которая уже была использована, возникает ошибка.</p><p>Объявление метода в интерфейсе диспетчеризации не может содержать никаких директив, кроме dispid. Параметр и тип результата должны быть автоматизируемы. Иначе говоря, они не могут иметь типы, отличные от Byte,Currency, Real, Double, Longint, Integer, Single, Smallint, AnsiString, WideString, TDateTime, Variant, OleVariant, WordBool или другого интерфейсного типа.</p><br><h3>Свойства интерфейса диспетчеризации</h3><p>Объявления свойств интерфейса диспетчеризации не могут содержать спецификаторов доступа. Они могут объявляться как свойства только для чтения или только для записи (read only или write only). Для указания ID диспетчеризации для свойства в его объявление необходимо включить директиву dispid, следом за которой указывается целочисленная константа. При указании константы, которая уже была использована, возникает ошибка. Свойства-массивы могут быть объявлены с директивой default. Использование прочих директив в объявлении свойств интерфейсов диспетчеризации не разрешается.</p><br><h3>Доступ к объектам автоматизации</h3><p>Связывание вызовов методов объектов автоматизации происходит в режиме выполнения программы и не требует наличия объявления предыдущих методов. Допустимость таких вызовов при компиляции не проверяется.</p><p>Следующий пример показывает вызовы автоматизированных методов. Функция CreateOleObject (объявленная в ComObj) возвращает ссылку IDispatch на объект автоматизации. Она совместима по присваиванию с переменной вариантного типа Word:</p><pre class="brush:pas">var
Word: Variant;
begin
Word := CreateOleObject('Word.Basic');
Word.FileNew('Normal');
Word.Insert('This is the first line'#13);
Word.Insert('This is the second line'#13);
Word.FileSaveAs('c:\temp\test.txt', 3);
end;</pre><p>Вы можете передавать параметры типа интерфейс в методы автоматизации.</p><p>Вариантные массивы с элементами типа varByte являются предпочтительным вариантом для передачи двоичных данных между контроллерами автоматизации и серверами. Данные из этих массивов не нуждаются в трансляции, а доступ к ним может быть эффективно организован при помощи подпрограмм VarArrayLock и VarArrayUnlock.</p><br><h3>Синтаксис вызова методов объектов автоматизации</h3><p>Синтаксис вызова методов объектов автоматизации или доступа к их свойствам схож с обычным вызовом методов или доступом к свойствам. Тем не менее, при вызове методов автоматизации могут быть использованы как позиционные, так и именованные параметры (некоторые сервера автоматизации не поддерживают именованных параметров).</p><p>Позиционный параметр – это обычное выражение. Именованный параметр состоит из идентификатора параметра, за которым указывается символ :=, за которым следует выражение. При вызове метода перед позиционными параметрами необходимо включать любые именованные параметры. Именованные параметры могут быть указаны в любом порядке.</p><p>Некоторые сервера автоматизации позволяют при вызове методов опускать параметры, принимая значения по умолчанию. Например:</p><pre class="brush:pas"> Word.FileSaveAs('test.doc');
Word.FileSaveAs('test.doc', 6);
Word.FileSaveAs('test.doc',,,'secret');
Word.FileSaveAs('test.doc', Password := 'secret');
Word.FileSaveAs(Password := 'secret', Name := 'test.doc');
</pre><p>Параметры для вызова метода автоматизации могут иметь типы: integer, real, string, Boolean и variant. Параметр передается по ссылке, в том случае, если выражение параметра состоит только из переменной или ссылки на переменную типа Byte, Smallint, Integer, Single, Double, Currency, System.TDateTime, AnsiString, WordBool или Variant. Если выражение не возвращает значение перечисленных типов или если это не только переменная, параметр передается по значению. Передача параметра по ссылке методу, принимающего параметр-значение, приводит к тому, что COM получает значение из ссылочного параметра. Передача параметра по значению в метод, принимающий ссылочный параметр, вызывает ошибку.</p><br><br />
<h3>Двойные интерфейсы</h3><p>Двойной интерфейс – это интерфейс, поддерживающий через автоматизацию связывание как во время компиляции, так и связывание времени выполнения. Двойные интерфейсы должны наследоваться от IDispatch.</p><p>Все методы двойного интерфейса (за исключением наследуемых от IInterface и IDispatch) должны использовать конвенцию safecall, а их параметры и результат должны иметь автоматизируемые типы (Byte, Currency, Real, Double, Real48, Integer, Single, Smallint, AnsiString, ShortString, System/TDateTime, Variant, OleVariant и WordBool.)</p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-5298421588419094262013-08-30T02:52:00.000+04:002013-08-30T02:52:04.705+04:00Обращение к интерфейсам<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Interface_References" target="_blank">Interface References</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><p>Если вы объявляете переменную интерфейсного типа, она может ссылаться на экземпляр любого класса, поддерживающего этот интерфейс. </p><a name='more'></a><br><br />
<h3>Реализация ссылок на интерфейс </h3><p>Переменные типа интерфейс позволяют вызывать методы интерфейса при том, что во время компиляции не известно, где реализован интерфейс. Тем не менее, они подчиняются следующим правилам:</p><ul><li>Выражение, возвращающее тип интерфейса, предоставляет доступ только к методам и свойствам, объявленным в интерфейсе, но не к прочим компонентам класса, реализующего интерфейс.</li>
<li>Выражение, возвращающее тип интерфейса, не может ссылаться на объект, класс которого реализует интерфейс-потомок, кроме тех случаев, когда класс (или его предок) явным образом реализует и интерфейс-предок. </li></ul><p>Например:</p><pre class="brush:pas"> type
IAncestor = interface
end;
IDescendant = interface(IAncestor)
procedure P1;
end;
TSomething = class(TInterfacedObject, IDescendant)
procedure P1;
procedure P2;
end;
// ...
var
D: IDescendant;
A: IAncestor;
begin
D := TSomething.Create; // работает!
A := TSomething.Create; // ошибка
D.P1; // работает!
D.P2; // ошибка
end;</pre><p>В этом примере А объявлена как переменная типа IAncestor. Поскольку в объявлении класса TSomething не указывается, что этот класс реализует интерфейс IAncestor, ссылка на экземпляр TSomething не может быть присвоена переменной A. Это можно будет сделать, только если изменить объявление Tsomething следующим образом:</p><pre class="brush:pas"> TSomething = class(TInterfacedObject, IAncestor, IDescendant)
// ...
</pre><p>В этом случае первая ошибка станет допустимым присваиванием. D объявлена как переменная типа IDescendant. Пока D ссылается на экземпляр класса TSomething, вы не сможете с ее помощью получить доступ к методу P2 класса Tsomething, так как этот метод не является методом IDescendant. Но если изменить объявление D следующим образом:</p><pre class="brush:pas"> D: TSomething;
</pre><p>Вторая ошибка станет корректным вызовом метода.</p><p>На платформе Win32 обращения к интерфейсам обычно обрабатываются подсчетом ссылок, реализованного через методы _AddRef и _Release , наследуемые от System.IInterface. При использовании механизма подсчета ссылок по умолчанию, когда к доступ к объекту выполняется только через интерфейсы, разрушать его вручную нет необходимости. Объект автоматически разрушается в тот момент, когда последняя ссылка на него выходит из области видимости. Некоторые классы поддерживают интерфейсы для изменения управления жизненным циклом, определенным по умолчанию, а некоторые гибридные объекты используют подсчет ссылок только в том случае, когда объект не имеет владельца.</p><p>Глобальные переменные типа интерфейс могут быть инициализированы только значением nil.<br />
<p>Для того, чтобы определить ссылается ли переменная интерфейсного типа на объект, можно передать ее в качестве параметра стандартной функции Assigned.</p><br><br />
<h3>Совместимость по присваиванию для интерфейсов</h3><p>Переменные типа класс совместимы по присваиванию с любым интерфейсным типом, поддерживаемым этим классом. Переменные интерфейсного типа совместимы по присваиванию с любым интерфейсным типом-предком. Значение nil может быть присвоено любой переменной типа интерфейс.</p><p>Переменной типа вариант может быть присвоено значение выражения интерфейсного типа. Если интерфейс имеет тип IDispatch или его потомка, значение кода типа вариантной переменной изменится на varDispatch. В остальных случаях код типа варианта будет varUnknown.</p><p>Значение переменных типа вариант, код типа которых равен varEmpty, varUnknown или varDispatch, может быть присвоено переменной типа IInterface. Значение переменных типа вариант с кодом типа varEmpty или varDispatch могут быть присвоены переменным с типом IDispatch.</p><br><h3>Преобразование типов интерфейсов</h3><p>Выражение типа интерфейс может быть преобразовано к типу вариант. Если интерфейс имеет тип IDispatch или его потомок, код типа полученного в результате преобразования варианта примет значение varDispatch. В остальных случаях вариант будет иметь код типа varUnknown.</p><p>Варианты с кодом типа varEmpty, varUnknown или varDispatch могут быть преобразованы к IInterface. Вариант с кодом типа varEmpty или varDispatch могут быть преобразованы к IDispatch.</p><br><h3>Запрос интерфейса</h3><p>Вы можете использовать оператор as для преобразования типа с проверкой. Такой способ известен как запрос интерфейса и позволяет получить выражение типа интерфейс из ссылки на объект или другой интерфейс основываясь на действительном типе (тип во время выполнения). Запрос интерфейса имеет вид:</p><pre class="brush:pas">object as interface</pre><p>где object – это выражение интерфейсного или вариантного типа или ссылка на класс, поддерживающий интерфейс, а interface – любой интерфейс, объявленный с GUID.</p><p>Запрос интерфейса возвращает значение nil, когда object имеет значение nil. В других случаях он передает GUID интерфейса в метод QueryInterface объекта, вызывая исключительную ситуацию, когда QueryInterface не возвращает ноль. Если QueryInterface возвращает ноль (это обозначает, что класс объекта поддерживает интерфейс), запрос интерфейса возвращает объекту ссылку на интерфейс.</p><br><br />
<h3>Преобразование ссылок на интерфейс в объекты</h3><p>Оператор as может также быть использован для преобразования ссылки на интерфейс обратно в объект, от которого она была получена. Такое преобразование возможно только для интерфейсов, полученных от объектов Delphi. Например:</p><pre class="brush:pas"> var
LIntfRef: IInterface;
LObj: TInterfacedObject;
begin
{Создание интерфейсного объекта и получения его интерфейса. }
LIntfRef := TInterfacedObject.Create();
{Преобразование интерфейса обратно в оригинальный объект. }
LObj := LIntfRef as TInterfacedObject;
end;</pre><p>Этот пример показывает, как получить первоначальный объект, от которого был получен интерфейс. Эта технология может оказаться полезной, когда получения ссылки на интерфейс просто недостаточно.</p><p>Оператор as вызывает исключительную ситуацию при неудачной попытке получения интерфейса заданного класса:</p><pre class="brush:pas"> var
LIntfRef: IInterface;
LObj: TInterfacedObject;
begin
{ Создаем интерфейсный объект и получаем его интерфейс. }
LIntfRef := TInterfacedObject.Create();
try
{Преобразуем интерфейс к TComponent. }
LObj := LIntfRef as TComponent;
except
Writeln('LIntfRef не ссылается на экземпляр TComponent');
end;
end;</pre><p>Вы можете использовать и обычное (небезопасное) преобразование типов для получения из ссылки на интерфейс ссылку на объект. Как и в случае с небезопасным преобразованием типов объектов, этот метод не вызывает никаких исключительных ситуаций. Различия между небезопасным преобразованием типов объектов и преобразованием тип интерфейс-объект заключаются в том, что в первом случае операция преобразования при несоответствии типов возвращает нормальный указатель, во втором результатом операции будет значение nil. Пример показывает небезопасное преобразование типов:</p><pre class="brush:pas"> var
LIntfRef: IInterface;
LObj: TInterfacedObject;
begin
{создание интерфейсного объекта и получение его интерфейса. }
LIntfRef := TInterfacedObject.Create();
{ Преобразование интерфейса к TComponent. }
LObj := TComponent(LIntfRef);
if LObj = nil then
Writeln('LIntfRef не ссылается на экземпляр TComponent');
{Преобразование интерфейса к TObject. }
LObj := TObject(LIntfRef);
if LObj <> nil then
Writeln('LIntfRef не ссылается на TObject (или его наследников).');
end;
</pre><p>Для того, чтобы избежать получения пустых ссылок при преобразовании следует использовать оператор is для проверки, может ли ссылка на интерфейс быть получена из заданного класса:</p><pre class="brush:pas"> if Intf is TCustomObject then ...
</pre><cite>Замечание: Убедитесь, что при небезопасном преобразовании типов (или применении операторов as и is) вы используете именно объекты Delphi.</cite><br />
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-31372866267106900332013-08-30T02:28:00.002+04:002013-08-30T02:28:38.008+04:00Реализация интерфейсов<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Implementing_Interfaces" target="_blank">Implementing Interfaces</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><p>Для того, чтобы начать использовать объявленный интерфейс, необходимо реализовать его поддержку в классе. Интерфейсы, поддерживаемые классом, указываются в его объявлении после указания класса-предка.</p><a name='more'></a><br />
<br><br />
<h3>Объявления классов</h3><p>Такое объявление имеет вид:</p><pre class="brush:pas">type className = class (ancestorClass, interface1, ..., interfaceN)
memberList
end;</pre><p>Следующий пример<br />
<pre class="brush:pas">type
TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
// ...
end;</pre><p>объявляет класс с именем TmemoryManager, который поддерживает интерфейсы IMalloc и IErrorInfo. Когда класс поддерживает интерфейс, он должен реализовывать (или наследовать реализацию) каждого метода, объявленного в интерфейсе.</p><p>Далее приведено объявление TInterfacedObject в модуле System (для платформы Win32):</p><pre class="brush:pas">type
TInterfacedObject = class(TObject, IInterface)
protected
FRefCount: Integer;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
class function NewInstance: TObject; override;
property RefCount: Integer read FRefCount;
end;</pre><p>TInterfacedObject поддерживает интерфейс Iinterface, то есть TInterfacedObject объявляет и реализует все три метода IInterface.</p><p>Классы, которые поддерживают интерфейсы, также могут быть использованы в качестве базовых классов (в первом примере объявлен класс TmemoryManager, являющийся прямым наследником TInterfacedObject.) На платформе Win32 каждый интерфейс наследуется от IInterface, а класс, поддерживающий интерфейсы, должен реализовывать методы QueryInterface, _AddRef, и _Release. TinterfacedObject, объявленный в модуле System, реализует эти методы, а значит он является базовым классом, подходящим для наследования классов, поддерживающих интерфейсы.</p><p>Когда интерфейс реализован, все его методы соотносятся с методами реализующего класса, которые имеют те же типы возвращаемых значений, те же конвенции вызова, одинаковое количество параметров, которые совпадают по типу и порядку следования. По умолчанию метод интерфейса сопоставляется с методом класса, который имеет такое же имя.</p><br><h3>Раздел сопоставления методов</h3><p>Вы можете перекрыть порядок сопоставления методов, включив в объявление класса раздел сопоставления методов. Когда класс поддерживает два и более интерфейса с одинаковыми именами методов, этот раздел поможет избежать конфликтов имен.</p><p>Раздел сопоставления имен имеет вид:<br />
<pre class="brush:pas">procedure interface.interfaceMethod = implementingMethod;
</pre><p>или:<br />
<pre class="brush:pas">function interface.interfaceMethod = implementingMethod;
</pre><p>где implementingMethod это метод, объявленный в классе или его предках. Сам implementingMethod может быть объявлен позже в объявлении класса, но не может относиться к классу-предку, объявленному в другом модуле и иметь область видимости private.</p><p>Например, объявление класса:</p><pre class="brush:pas">type
TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
function IMalloc.Alloc = Allocate;
procedure IMalloc.Free = Deallocate;
// ...
end;</pre><p>сопоставляет методы Alloc и Free интерфейса Imalloc с методами Allocate и Deallocate в классе TMemoryManager.</p><p>Раздел сопоставления методов не может изменять сопоставление методов, указанное в классе-предке.</p><br><h3>Изменение наследуемой реализации</h3><p>Классы-потомки могут изменять реализацию методов, перекрывая ее. Для этого необходимо, чтобы реализующие методы были динамическими или виртуальными.</p><p>Класс может повторно реализовать наследуемый от класса-предка интерфейс целиком. Для этого потребуется повторно указать имя интерфейса в объявлении класса-потомка. Например:</p><pre class="brush:pas">type
IWindow = interface
['{00000115-0000-0000-C000-000000000146}']
procedure Draw;
// ...
end;
TWindow = class(TInterfacedObject, IWindow)
// TWindow реализует pocedure Draw интерфейса IWindow ;
// ...
end;
TFrameWindow = class(TWindow, IWindow)
// TFrameWindow повторно реализует procedure Draw интерфейса IWindow ;
// ...
end;
</pre><p>Повторная реализация скрывает наследуемую реализацию этого интерфейса. То есть раздел сопоставления методов в классе-предке перестает действовать при перекрытии реализации интерфейса.</p><br><h3>Реализация интерфейсов путем делегирования (только для Win32)</h3><p>На платформе Win32 директива implements позволяет делегировать реализацию интерфейса свойству в реализующем классе. В примере</p><pre class="brush:pas">property MyInterface: IMyInterface read FMyInterface implements IMyInterface;</pre><p>объявлено свойство с именем MyInterface , которое реализует интерфейс IMyInterface.</p><p>Директива implements должна быть последним спецификатором в объявлении свойства и может содержать несколько имен интерфейсов, разделенных запятыми. Делегирующее свойство:</p><ul><li>Должно иметь тип класса или интерфейса.</li>
<li>Не может быть свойством-массивом или иметь спецификатор индекса.</li>
<li>Должно иметь спецификатор read. Если свойство имеет метод для чтения, он должен быть объявлен с конвенцией вызова по умолчанию register и не может быть динамическим (тем не менее, он может быть виртуальным), а его объявление не может содержать директиву message.</li></ul><p>Класс, который вы используете для реализации делегируемого интерфейса, должен наследоваться от TAggregationObject.</p><br />
<br><h3>Делегирование свойству интерфейсного типа (только для Win32)</h3><p>Если делегируемое свойство имеет тип интерфейса, этот интерфейс, или интерфейс, от которого он наследуется, должен быть упомянут в списке предков класса, в котором объявляется это свойство. Делегируемое свойство должно возвращать объект, класс которого полностью реализует интерфейс, указанный в директиве implements. При этом его объявление не должно содержать раздел переопределения методов. Например:</p><pre class="brush:pas">type
IMyInterface = interface
procedure P1;
procedure P2;
end;
TMyClass = class(TObject, IMyInterface)
FMyInterface: IMyInterface;
property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
end;
var
MyClass: TMyClass;
MyInterface: IMyInterface;
begin
MyClass := TMyClass.Create;
MyClass.FMyInterface := ...// какой-либо объект, класс которого поддерживает IMyInterface
MyInterface := MyClass;
MyInterface.P1;
end;</pre><br><h3>Делегирование свойству типа класс (только для Win32)</h3><p>Если делегирующее свойство имеет тип класса, поиск методов, реализующих указанный интерфейс, в первую очередь выполняется в этом классе и его предках, и только потом такой поиск выполняется в классе, содержащем свойство, и его предках.</p><p>Таким образом, существует возможность реализовать одни методы в классе, указанном в объявлении свойства, а остальные – в классе, содержащем свойство. Разделы переопределения методов можно использовать как обычно для разрешения неопределенностей или для указания конкретного метода. Интерфейс не может быть реализован более, чем одним свойством типа класс. Например: </p><pre class="brush:pas">type
IMyInterface = interface
procedure P1;
procedure P2;
end;
TMyImplClass = class
procedure P1;
procedure P2;
end;
TMyClass = class(TInterfacedObject, IMyInterface)
FMyImplClass: TMyImplClass;
property MyImplClass: TMyImplClass read FMyImplClass implements IMyInterface;
procedure IMyInterface.P1 = MyP1;
procedure MyP1;
end;
procedure TMyImplClass.P1;
// ...
procedure TMyImplClass.P2;
// ...
procedure TMyClass.MyP1;
// ...
var
MyClass: TMyClass;
MyInterface: IMyInterface;
begin
MyClass := TMyClass.Create;
MyClass.FMyImplClass := TMyImplClass.Create;
MyInterface := MyClass;
MyInterface.P1; // вызывает TMyClass.MyP1;
MyInterface.P2; // вызывает TImplClass.P2;
end;
</pre></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-87709317134827481342013-08-30T02:28:00.001+04:002013-08-30T02:28:28.224+04:00Интерфейсы объектов<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Object_Interfaces" target="_blank">Object Interfaces (Delphi)</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><p>Интерфейс объекта (или просто интерфейс) определяет методы, которые могут быть реализованы классом. Объявление интерфейсов схоже с объявлением классов, но их отличие в том, что вы не можете создавать экземпляры интерфейса и, кроме того, они не могут иметь собственных объявлений методов. Реализация методов интерфейса – это скорее ответственность класса, который поддерживает интерфейс. Переменная типа интерфейс может хранить ссылку на объект, класс которого реализует этот интерфейс. Но через эту переменную вы сможете обратиться только к методам, которые объявлены в интерфейсе.</p><a name='more'></a><br />
<p>Интерфейсы дают некоторые преимущества множественного наследования, позволяя при этом избежать семантических трудностей. Они также являются неотъемлемой частью моделей распределенных объектов (таких как SOAP). Применяя такую модель, объекты, поддерживающие интерфейсы, могут взаимодействовать с объектами, разработанными на C++, Java и других языках.</p><br><br />
<h3>Интерфейсные типы</h3><p>Интерфейсы, как и классы, могут быть объявлены только во внешней области видимости программы или модуля. Они не могут объявляться внутри процедуры или функции. Объявление интерфейсного типа имеет вид:</p><pre class="brush:pas">type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;
</pre><cite>Предупреждение: ancestorInterface и спецификация GUID обязательны для поддержки совместимости с Win32 COM. Если доступ к интерфейсу должен выполняться через COM, - необходимо определить ancestorInterface и GUID.</cite><br />
<p>Во многих отношениях объявления интерфейсов схожи с объявлениями классов, но содержат следующие ограничения:</p><ul><li>memberList может содержать только методы и свойства. Объявление полей в интерфейсах запрещено.</li>
<li>По причине отсутствия полей спецификаторы свойств read и write должны содержать методы.</li>
<li>Все компоненты (members) интерфейса имеют видимость public. Спецификаторы видимости и хранения (visibility and storage specifiers) запрещены. (Свойство-массив может быть объявлено как свойство по умолчанию.)</li>
<li>Интерфейсы не имеют конструкторов или деструкторов. Вы не можете создавать экземпляров интерфейса кроме как при помощи классов, которые их реализуют.</li>
<li>Методы могут быть объявлены как virtual, dynamic, abstract или override. Поскольку интерфейсы не содержат реализации собственных методов, эти обозначения не имеют значения.</li></ul><p>Далее приведен пример объявления интерфейса:</p><pre class="brush:pas">type IMalloc = interface(IInterface)
['{00000002-0000-0000-C000-000000000046}']
function Alloc(Size: Integer): Pointer; stdcall;
function Realloc(P: Pointer; Size: Integer): Pointer; stdcall;
procedure Free(P: Pointer); stdcall;
function GetSize(P: Pointer): Integer; stdcall;
function DidAlloc(P: Pointer): Integer; stdcall;
procedure HeapMinimize; stdcall;
end;
</pre><p>В некоторых объявлениях интерфейсов зарезервированное слово interface заменяется на dispinterface.</p><br><br />
<h3>IInterface и наследование</h3><p>Интерфейс, как и класс, наследует методы своего предка. Но в отличии от классов, интерфейсы не реализуют методов. То, что наследует интерфейс, - это обязательство реализации методов, обязательство, которое передается классам, поддерживающим интерфейс.</p><p>Объявление интерфейса определяет интерфейс-предок. Если такового не указано, интерфейс будет прямым потомком IInterface, определенном в модуле System и являющимся основным предком всех интерфейсов. На платформе Win32, интерфейс IInterface объявляет три метода: QueryInterface, _AddRef, и _Release.</p><cite>Замечание: IInterface эквивалентен IUnknown. Для платформо-независимых приложений следует использовать Iinterface, а IUnknown – для специфических программ, зависящих от Win32.</cite><br />
<p>QueryInterface предоставляет возможность получения ссылки на интерфейсы, которые поддерживает объект. _AddRef и _Release обеспечивают управление памятью при работе интерфейса. Самый простой способ реализовать эти методы – это наследовать класс от TinterfacedObject, объявленном в модуле System. Также существует возможность обойтись без этих методов, реализуя их как пустые функции. Однако для COM объектов управление должно осуществляться через _AddRef и _Release.</p><cite>Предупреждение: QueryInterface, _AddRef и _Release необходимы для поддержки Win32 COM совместимости. Если доступ к вашему интерфейсу должен выполняться через COM, эти методы должны быть реализованы.</cite><br />
<br><h3>Идентификация интерфейсов и GUID</h3><p>Объявление интерфейса может содержать глобальный уникальный идентификатор(globally unique identifier - GUID), который представляет собой строковый литерал, предшествующий список компонентов и заключенный в скобки. GUID имеет следующий вид:</p><pre class="brush:pas"> ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
</pre><p>Где каждый x – это шестнадцатеричная цифра (от 0 до 9 или от A до F). Редактор библиотеки типов (Type Library editor) автоматически генерирует для создаваемых интерфейсов. Кроме того, вы можете сгенерировать GUID самостоятельно, нажав комбинацию Ctrl+Shift+G в редакторе кода.</p><p>GUID – это 16-байтовое двоичное значение, которое идентифицирует интерфейс. Если у интерфейса есть GUID, вы сможете получить ссылки на реализации интерфейса при помощи механизма запросов интерфейса.</p><cite>Замечание: GUID используются только для совместимости с COM.</cite><br />
<p>Типы TGUID и PGUID, объявленные в модуле System, применяются управления GUID.</p><pre class="brush:pas"> type
PGUID = ^TGUID;
TGUID = packed record D1: Longword;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;
</pre><cite>Замечание: модуль SysUtils содержит перегружаемую функцию с именем Supports, которая возвращает true или false в зависимости от того, поддерживает ли класс или экземпляр класса определенный интерфейс, идентифицируемый GUID. Функция Supports используется как операторы Delphi is и as, с единственным значимым отличием – функция Supports принимает за правый операнд либо GUID, либо интерфейсный тип, связанный с GUID, в то время как is и as принимают имя типа. </cite><br />
<p>Функция Supports может вызываться двумя способами:</p><pre class="brush:pas">if Supports(Allocator, IMalloc) then ...
</pre><p>или:<br />
<pre class="brush:pas">if Supports(Allocator, IID_IMalloc) then ...
</pre><br><h3>Конвенции вызова для интерфейсов</h3><p>Конвенция вызова по умолчанию для методов интерфейсов - register, но интерфейсы, используемые в разных модулях (а особенно, если они написаны на разных языках), должны объявлять все методы с конвенцией stdcall. На платформе Win32 вы можете использовать конвенцию safecall для реализации методов двойных интерфейсов.</p><br><h3>Свойства интерфейсов</h3><p>Получить доступ к свойствам, объявленным в интерфейсе, можно только через выражения интерфейсного типа: к ним нельзя обратиться через переменную типа класс. Более того, свойства интерфейсов видимы только внутри программ, в которых интерфейс объявлен.</p><p>Поскольку в объявлении интерфейса запрещены поля, спецификаторы свойств read и write должны содержать методы.</p><br><h3>Отложенные объявления</h3><p>Объявление интерфейса, которое заканчивается зарезервированным словом interface, за которым следует точка с запятой(;), без указания предка, GUID или списка компонентов, называется отложенным объявлением. Такое объявление должно быть разрешено в текущей секции объявления типов объявлением такого же интерфейса. Другими словами, между отложенным и окончательным объявлением не должно появиться ничего, кроме объявления других типов.</p><p>Отложенные объявления позволяют создавать взаимозависимые интерфейсы. Например:</p><pre class="brush:pas"> type
IControl = interface;
IWindow = interface
['{00000115-0000-0000-C000-000000000044}']
function GetControl(Index: Integer): IControl;
//. . .
end;
IControl = interface
['{00000115-0000-0000-C000-000000000049}']
function GetWindow: IWindow;
//. . .
end;</pre><p>Взаимонаследуемые интерфейсы запрещены. То есть нельзя создать интерфейс Iwindow, наследуемый от IControl и наследовать IControl от IWindow.</p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-20154330032694150602013-04-12T23:02:00.000+04:002013-04-12T23:03:28.367+04:00Кросс-платформенные библиотеки<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Cross-Platform_Shared_Libraries" target="_blank">Cross-Platform Shared Libraries</a> из справочной системы Delphi</cite><br />
<br />
<div align = "justify"><h2>Совместное использование библиотек в приложениях Mac OS X</h2><p>При разработке кросс-платформенных библиотек в Delphi, необходимо изучить некоторые требования, налагаемые платформами. В особенности это касается функций в библиотеках для OS X, которые необходимо вызывать и загружать динамически. Необходимо помнить, что:</p><ul><li>Имена динамически загружаемых функций должны начинаться с нижнего подчеркивания ('_').</li>
<li>Инструкции <b>exports</b> должны быть перемещены из файлов .dpr в файлы .pas.</li>
</ul><p>Эти два правила разъясняются далее.</p><a name='more'></a><br />
<h3>Динамически загружаемые функции для OS X требуют, чтобы их имена начинались с нижнего подчеркивания ('_')</h3><p>Рассмотрим соглашение об именовании для целевой платформы OS X: оно требует наличия символа нижнего подчеркивания в начале названий функций, которые будут экспортированы в OS X.</p><p>Это правило необходимо соблюдать в приложениях для OS X, поскольку стандартная библиотечная функция dlsym(), схожая по своему назначению с GetProcAddress() в WinAPI, добавляет в начало имен функций, доступ к которым выполняется динамически, символ нижнего подчеркивания. Библиотека создается обычным образом и статически связывается при компиляции, но при динамическом вызове функций в программе, доступ к ним будет невозможен при отсутствии нижнего подчеркивания в названии функции.</p><h3>Инструкции Exports недопустимы в файлах .dpr </h3><p>При разработке библиотек для Mac OS X, переместите инструкции <b>exports</b> из файла.dpr, содержащего код библиотеки, в файлы .pas, в которых объявляются экспортируемые функции.</p><h3>Пример программного кода</h3><p>Например, приведенный ниже модуль предназначен для экспорта функции, имя которой изменяется в зависимости от целевой платформы:</p><ul><li>Windows: TestProc()</li>
<li>OS X: _TestProc()</li>
</ul><pre class="brush:pas">unit uTestLib;
interface
{$IFDEF MACOS}
function _TestProc(var I: Integer): Integer; cdecl;
{$ELSE}
function TestProc(var I: Integer): Integer; cdecl;
{$ENDIF}
exports
{$IFDEF MACOS}
_TestProc;
{$ELSE}
TestProc;
{$ENDIF}
implementation
{$IFDEF MACOS}
function _TestProc(var I: Integer): Integer; cdecl; export;
{$ELSE}
function TestProc(var I: Integer): Integer; cdecl; export;
{$ENDIF}
begin
Result := I;
end;
end.</pre><p>Обращение к функции в OS X одним из способов:</p><ul><li>System.SysUtils.GetProcAddress()</li>
<li>Posix.Dlfcn.dlsym()</li>
</ul><p>В данном случае начальный символ нижнего подчеркивания не используется, поскольку OS X предполагает его наличие у всех динамически загружаемых функций и добавляет его автоматически.</p><cite>Замечание: Это требование касается динамически загружаемых функций и не относится к функциям, линкуемым статически с применением директивы <b>external</b>, добавляемой к объявлению функции. Имена статически линкуемых функций также могут начинаться с символа нижнего подчеркивания (обычно в OS X это так и есть), но это не является обязательным, как в случае с динамически загружаемыми функциями.</cite><br />
<p>Также необходимо учитывать, что в библиотеках, разрабатываемых для OS X, раздел <b>exports</b>, расположенный в главном файле библиотеки.dpr не распознается. Раздел <b>exports</b> для экспортируемых функций должен быть включен в файл модуля, как это показано в приведенном выше примере.</p><h2>Прочие условия</h2><h3>Компиляция библиотек с включением пакетов времени исполнения</h3><p>И библиотеки (DLL) и приложения должны компилироваться с включением пакетов времени исполнения. В противном случае все они будут иметь свою копию глобальных объектов, таких как Platform и Application.</p><h3>Вызов LoadLibrary</h3><p>LoadLibrary должна вызываться после запуска приложения. Загрузка DLL при инициализации модулей не работает.</p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-4909645972594219122013-04-12T22:37:00.000+04:002013-04-12T22:37:14.324+04:00Пакеты<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Packages" target="_blank">Packages (Delphi)</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><p>Пакеты обычно являются наиболее предпочтительным способом для экспорта, когда речь идет о чем-то более сложном, нежели функции и процедуры. Библиотеки следует использовать только в тех случаях, когда требуется обеспечить совместимость на уровне приложений.</p><a name='more'></a><br />
<h2>Понятие пакетов</h2><p>Пакет – это специальная скомпилированная библиотека, которая используется приложениями и/или IDE. Пакеты позволяют упорядочить приложение без изменения его программного кода. Часто это называют "дроблением приложения".</p><p>Пакеты времени выполнения (runtime packages) обеспечивают функциональность приложения при его запуске. Пакеты разработки (design time packages) применяются при установке компонентов в IDE и разработке специализированных редакторов свойств для пользовательских компонентов. Один и тот же пакет может содержать функции времени выполнения и разработки. Кроме того, пакет разработки в своем разделе <b>requires</b> может содержать ссылки на пакеты времени выполнения.</p><p>На платформе Win32, файлы пакетов имеют расширение .bpl (Borland package library – пакетная библиотека Borland).<br />
Обычно пакеты загружаются статически при запуске приложения. Но для динамической загрузки можно использовать и подпрограммы LoadPackage из UnloadPackage (из модуле SysUtils).</p><cite>Замечание: при подключении пакета в приложение каждый его модуль, ссылающийся на модули пакета, должен содержать имена этих модулей в разделе uses.</cite><br />
</br><h2>Файлы программного кода пакетов и объявление пакетов</h2><p>Каждый пакет объявляется в отдельном файле. Чтобы избежать путаницы с прочими файлами Delphi, такой файл должен быть сохранен с расширением .dpk. Файл программного кода пакета не содержит объявлений типов, данных, процедур или функций. Он содержит:</p><ul><li>Имя пакета</li>
<li>Список пакетов, требуемых для этого пакета (связанные с ним пакеты). </li>
<li>Список файлов модулей, которые включены в пакет или связаны с ним при компиляции. Пакет по существу является оберткой для модулей, обеспечивающих его функциональность.</li></ul><p>Объявление пакета имеет вид:</p><pre class="brush:pas">package packageName;
requiresClause;
containsClause;
end.</pre><p>Где packageName – это любой допустимый идентификатор. Разделы requiresClause и containsClause являются необязательными. Следующий пример демонстрирует объявление пакета DATAX:</p> <pre class="brush:pas">package DATAX;
requires
rtl,
contains Db, DBLocal, DBXpress, ... ;
end.</pre><p>В разделе <b>requires</b> перечислены внешние пакеты, использованные объявляемым пакетом. Этот раздел состоит из директивы <b>requires</b>, за которой следует список имен пакетов, разделяемых запятыми. Если пакет не ссылается на другие пакеты, раздел <b>requires</b> не нужен.</p><p>Раздел <b>contains</b> определяет файлы модулей, которые необходимо скомпилировать в пакете. Он состоим из директивы <b>contains</b>, за которой следует список имен модулей, разделяемых запятыми. За любым именем модуля может следовать зарезервированное слово in имя файла с указанием (или без указания) полного пути к нему, заключенное в одинарные кавычки. Путь к файлу может быть как абсолютным, так и относительным. Например:</p><pre class="brush:pas">contains MyUnit in 'C:\MyProject\MyUnit.pas';</pre><cite>Замечание: Доступ к локальным переменным потоков (объявленным в модулях пакета при помощи threadvar) из приложений и модулей, подключающих этот пакет, невозможен. </cite></br><br />
<h3>Файлы пакетов</h3><p>Скомпилированный пакет состоит из нескольких файлов. Например, исходный файл проекта с именем DATAX - это DATAX.DPK. Из него при компиляции генерируется исполняемый файл и двоичный образ с именами DATAX.BPL и DATAX.DCP. </p><p>Имя DATAX используется при обращении к пакету в разделах <b>requires</b> других пакетов или при использовании пакета в приложении. Имена пакетов должны быть уникальными в рамках одного проекта.</p><h3>Раздел requires</h3><p>В разделе <b>requires</b> перечисляются внешние пакеты, которые подключаются в текущий пакет. Этот раздел функционирует также, как и раздел uses в файле модуля. Внешний пакет, указанный в разделе <b>requires</b>, автоматически линкуется при компиляции в любое приложение, которое подключает текущий пакет или модуль из внешнего пакета.</p><p>Если файлы модулей, содержащихся в одном пакете, имеют ссылки на модули в других пакетах, эти пакеты должны быть перечислены в разделе <b>requires</b> первого пакета. Если эти имена этих пакетов отсутствуют в разделе <b>requires</b>, компилятор загружает запрошенные модули из файлов .dcu.</p><h3>Избегание циклических ссылок</h3><p>В разделах <b>requires</b> запрещены циклические ссылки. Это обозначает, что</p><ul><li>Пакет не может ссылаться сам на себя в разделе <b>requires</b>.</li>
<li>Цепочки ссылок должны заканчиваться без повторения пакетов. Если пакет A ссылается на пакет B, то пакет B не может ссылаться на A; если пакет A ссылается на B, а пакет B ссылается на C, то пакет C не может ссылаться на пакет A.</li></ul><h3>Двойные ссылки на пакеты</h3><p>Компилятор игнорирует повторные ссылки в разделе <b>requires</b>. Тем не менее, для порядка и удобства чтения программы двойные ссылки лучше удалять.</p><h3>Раздел contains</h3><p>Раздел <b>contains</b> определяет файлы модулей, которые должны быть скомпилированы в пакет. Расширения имен файлов в разделе <b>contains</b> указывать не следует.</p><h3>Избегание избыточных ссылок</h3><p>Пакет не может быть указан в разделе <b>contains</b> другого пакета или в разделе uses программного модуля.</p><p>Все модули, включенные в раздел <b>contains</b> пакета (прямое включение) или разделы uses его модулей (косвенное включение) помещаются в пакет во время компиляции. Модули, содержащиеся в пакете, не могут быть включены в состав других пакетов, перечисленных в разделе <b>requires</b> этого пакета.</p><p>Запрещается включение (прямое или косвенное) одного и того же модуля в нескольких пакетах, используемых в одном приложении.</p><h2>Компиляция пакетов</h2><p>Обычно компиляция пакетов выполняется из IDE, при этом файлы .dpk генерируются Менеджером Проектов (Project Manager). Вы можете компилировать файлы .dpk и непосредственно из командной строки. Если в проекте содержится пакет, то при компиляции этого проекта, пакет будет автоматически перекомпилирован при необходимости.</p><h3>Файлы, создаваемые автоматически</h3><p>В следующей таблице перечислены файлы, создаваемые при успешной компиляции пакета.</p><table class="dttbl" width="700"><tr><th class="dtth">Расширение файла</th><th class="dtth">Содержимое</th></tr>
<tr><td class="dttd">DCP</td><td class="dttd">Двоичный файл, содержащий заголовок пакета и сцепку всех файлов.dcu (Win32), включенных в пакет. Файлы .dcp или .dcp создаются для каждого пакета. Файл получает такое же имя, что и программный файл пакета с расширением .dpk.</td></tr>
<tr><td class="dttd">BPL</td><td class="dttd">Пакет времени исполнения. Этот файл представляет собой динамически загружаемую библиотеку со специфической функциональностью, добавленной компанией Embarcadero. Файл получает такое же имя, что и программный файл пакета с расширением .dpk.</td></tr>
</table></br><br />
<h3>Директивы компилятора, связанные с пакетами</h3><p>В следующей таблице перечислены директивы компилятора, добавление которых в программный код будет влиять на компиляцию пакетов.</p><table class="dttbl" width="700"><tr><th class="dtth">Директива</th><th class="dtth">Назначение</th></tr>
<tr><td class="dttd">{$IMPLICITBUILD OFF}</td><td class="dttd">Предотвращает автоматическую перекомпиляцию пакета. Эту директиву следует использовать в файлах.dpk при компиляции пакетов, программный код которых не будет распространяться или обеспечивает низкоуровневую функциональность, которая изменяется редко.</td></tr>
<tr><td class="dttd">{$G-} или {$IMPORTEDDATA OFF}</td><td class="dttd">Отключает создание ссылок импортируемых данных. Эта директива увеличивает эффективность работы с памятью, но накладывает ограничения на ссылки из модуля на переменные, объявленные в других пакетах.</td></tr>
<tr><td class="dttd">{$WEAKPACKAGEUNIT ON}</td><td class="dttd">Слабая упаковка модулей.</td></tr>
<tr><td class="dttd">{$DENYPACKAGEUNIT ON}</td><td class="dttd">Предотвращает помещение модуля в пакет.</td></tr>
<tr><td class="dttd">{$DESIGNONLY ON}</td><td class="dttd">Компиляция пакета для установки в IDE. (Директива указывается в файле.dpk.)</td></tr>
<tr><td class="dttd">{$RUNONLY ON}</td><td class="dttd">Скомпилированный пакет может быть использован только как пакет времени выполнения (Директива указывается в файле.dpk.).</td></tr>
</table><p>Включение директивы {$DENYPACKAGEUNIT ON} в программный код запрещает упаковку модуля. Включение директивы {$G-} или {$IMPORTEDDATA OFF} может предотвратить использование пакета другими пакетами в одном приложении.</p><p>Прочие директивы компилятора могут быть включены в программный файл пакета при необходимости.</p><h3>Компиляция пакетов из командной строки</h3><p>В следующей таблице перечислены ключи компилятора, связанные с компиляцией пактетов.</p><table class="dttbl" width="700"><tr><th class="dtth">Ключ</th><th class="dtth">Назначение</th></tr>
<tr><td class="dttd">-$G-</td><td class="dttd">Отключает создание ссылок импортируемых данных. Эта директива увеличивает эффективность работы с памятью, но накладывает ограничения на ссылки из пакета на переменные, объявленные в других пакетах.</td></tr>
<tr><td class="dttd">LE path</td><td class="dttd">Определяет директорий, куда будет помещен скомпилированный пакет.</td></tr>
<tr><td class="dttd">LN path</td><td class="dttd">Определяет директорий, куда будет помещены файлы dcp или dcp.</td></tr>
<tr><td class="dttd">LUpackageName[;packageName2;...]</td><td class="dttd">Определяет дополнительные пакеты времени выполнения для подключения в приложение. Используется при компиляции проекта.</td></tr>
<tr><td class="dttd">Z</td><td class="dttd">Предотвращает автоматическую перекомпиляцию пакета. Эту директиву следует использовать при компиляции пакетов, программный код которых не будет распространяться или обеспечивает низкоуровневую функциональность, которая изменяется редко.</td></tr>
</table><p>Применение ключа -$G- может предотвратить использование пакета другими пакетами в одном приложении.</p><p>Прочие ключи могут быть использованы при компиляции пакета при необходимости. </p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-91357272105052961082013-04-12T01:37:00.001+04:002013-04-12T01:37:39.421+04:00Разработка динамически загружаемых библиотек<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Writing_Dynamically_Loaded_Libraries" target="_blank">Writing Dynamically Loaded Libraries</a> из справочной системы Delphi</cite><br><br />
<div align = "justify"><cite>Замечание: Библиотеки в части экспорта ограничены значительно больше, чем пакеты. Библиотеки не могут экспортировать константы типы и обычные переменные. То есть класс, объявленный в библиотеке, не может быть виден в программе, которая ее использует.</cite><br />
<p>Для экспорта всего, что не является процедурой или функцией, рекомендуется применять пакеты. Библиотеки следует рассматривать только в том случае, если необходима совместимость с другими программами.</p><a name='more'></a><br />
<br />
<h3>Использование раздела Export в библиотеках</h3><p>Основной файл динамически загружаемой библиотеки начинается с зарезервированного слова <b>library</b>, в остальном он идентичен основному файлу программы (который начинается с зарезервированного слова <b>program</b>). </p><p>Для экспорта в другие библиотеки или программы доступны только подпрограммы, которые явным образом экспортируются библиотекой. Следующий пример показывает библиотеку, экспортирующую две функции: Min и Max.</p><pre class="brush:pas">library MinMax;
function Min(X, Y: Integer): Integer; stdcall;
begin
if X < Y then Min := X else Min := Y;
end;
function Max(X, Y: Integer): Integer; stdcall;
begin
if X > Y then Max := X else Max := Y;
end;
exports
Min,
Max;
begin
end.</pre><p>Если вы хотите, чтобы библиотека была доступна программам, разработанным на других языках, - самым безопасным способом будет включить директиву <b>stdcall</b> в объявление экспортируемых функций. Другие языки могут не поддерживать конвенцию вызова <b>register</b>, которая является конвенцией вызова по умолчанию в Delphi. </p><p>Библиотеки могут состоять из нескольких модулей. В этом случае часто в основной файл библиотеки включают только разделы <b>uses</b>, <b>exports</b> и код инициализации. Например:</p><pre class="brush:pas">library Editors;
uses EdInit, EdInOut, EdFormat, EdPrint;
exports
InitEditors,
DoneEditors name Done,
InsertText name Insert,
DeleteSelection name Delete,
FormatSelection,
PrintSelection name Print,
.
.
.
SetErrorHandler;
begin
InitLibrary;
end.</pre><p>Разделы <b>exports</b> в модуле можно разместить в секции <b>interface</b> или <b>implementation</b>. Библиотека, которая включает в себя такой модуль, автоматически экспортирует все подпрограммы из его разделов <b>exports</b> и не требует наличия собственного раздела <b>exports</b>.</p><p>Подпрограмма экспортируется при включении ее в раздел <b>exports</b>, который имеет вид:</p><pre class="brush:pas">exports entry1, ..., entryn;</pre><p>каждый элемент этого списка состоит из имени процедуры, функции или переменной (которая должна быть объявлена до раздела <b>exports</b>), за которым следует список ее параметров (только в том случае, если вы экспортируете перегруженную функцию) и опциональный спецификатор имени. Кроме того, имя процедуры или функции можно специфицировать указанием имени модуля.</p><p>Элементы списка могут также включать директиву resident, которая поддерживается для обеспечения обратной совместимости и игнорируется компилятором.</p><p>На платформе Win32 спецификатор <b>index</b> состоит из директивы <b>index</b>, за которой следует числовая константа в диапазоне от 1 до 2,147,483,647. (Для повышения эффективности работы программы следует использовать небольшие значения индекса). Если элемент в списке не имеет спецификатора индекса, подпрограмме автоматически присваивается ее номер в таблице экспорта. </p><cite>Замечание: Использование спецификаторов <b>index</b> не рекомендовано и поддерживается только для обратной совместимости. Их применение может привести к проблемам в работе инструментария разработчика.</cite><br />
<p>Спецификатор имени состоит из директивы <b>name</b>, за которой следует строковая константа. Если элемент списка не содержит такого спецификатора, подпрограмма экспортируется под своим оригинальным именем (как она была объявлена) с учетом его написания и регистра символов в названии. Спецификатор <b>name</b> следует использовать в том случае, если вы хотите экспортировать подпрограмму под именем, отличным от ее объявления. Например:</p><pre class="brush:pas">exports
DoSomethingABC name 'DoSomething';</pre><p>Когда вы экспортируете перегруженную функцию или процедуру из динамически загружаемой библиотеки, в разделе <b>exports</b> необходимо указать ее список параметров. Например:</p><pre class="brush:pas">exports
Divide(X, Y: Integer) name 'Divide_Ints',
Divide(X, Y: Real) name 'Divide_Reals';</pre><p>На платформе Win32 при экспорте перегружаемых подпрограмм не следует указывать спецификаторы индексов.</p><p>Раздел <b>exports</b> может быть включен любое количество раз в любую часть объявления программы или библиотеки, а также в секции <b>interface</b> и <b>implementation</b> модуля. Программы редко содержат раздел <b>exports</b>.</p><h3>Код инициализации в библиотеке</h3><p>Инструкции в блоке библиотеки представляют собой код инициализации. Эти инструкции выполняются однократно в момент загрузки библиотеки. Обычно они выполняют задачи регистрации классов окон и инициализации переменных. В коде инициализации библиотеки можно также, используя переменную DllProc, выполнить установку процедуру точки входа. Переменная DllProc схожа с процедурой выхода, которая описана в разделе Exit procedures. Процедура точки входа выполняется в момент загрузки или выгрузки библиотеки.</p><p>Код инициализации библиотеки может передать информацию об ошибке, устанавливая значение, отличное от нуля для переменной ExitCode. ExitCode объявлена в модуле System и по умолчанию имеет значение 0, указывая на успешную инициализацию. Если код инициализации библиотеки устанавливает для ExitCode ненулевое значение, библиотека выгружается, а вызывающее приложение получает уведомление об ошибке. Вызывающее приложение получает такое же уведомление в случае, когда при выполнении кода инициализации, возникает необработанная исключительная ситуация.</p><p>Далее приведен пример библиотеки с кодом инициализации и процедурой точки входа:</p><pre class="brush:pas">library Test;
var
SaveDllProc: Pointer;
procedure LibExit(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
begin
.
. // library exit code
.
end;
SaveDllProc(Reason); // call saved entry point procedure
end;
begin
.
. // library initialization code
.
SaveDllProc := DllProc; // save exit procedure chain
DllProc := @LibExit; // install LibExit exit procedure
end.</pre><p>DllProc вызывается при первой загрузке библиотеки в память, при запуске или остановке потока, или при выгрузке библиотеки. Секции инициализации модулей, подключаемых библиотекой, выполняются перед выполнением кода инициализации библиотеки, а секции финализации – после выполнения процедуры точки входа.</p><h3>Глобальные переменные в библиотеке</h3><p>Глобальные переменные, объявленные в библиотеке, не могут быть импортированы приложением Delphi. </p><p>Библиотека может использоваться несколькими приложениями одновременно, но каждое приложение получает копию библиотеки в своем собственном пространстве процессов со своим собственным набором глобальных переменных. При работе с несколькими библиотеками (или с несколькими экземплярами одной и той же библиотеки) для разделения памяти, они должны использовать файлы разметки памяти. Для получения более подробной информации по этому вопросу следует обращаться к документации по вашей системе.</p><h3>Библиотеки и системные переменные</h3><p>Кроме того, для разработчиков библиотек представляют особый интерес еще несколько переменных, объявленных в модуле System. Для того, чтобы определить, какой программный код выполняется в текущий момент, можно воспользоваться переменной IsLibrary. IsLibrary всегда имеет значение False в приложении и True – в библиотеке. В течение всего времени жизни библиотеки переменная HInstance содержит дескриптор экземпляра (instance handle). При работе с библиотекой значение CmdLine всегда nil.</p><p>Переменная DLLProc позволяет библиотеке выполнять мониторинг обращений операционной системы к точке входа библиотеки. Эта особенность обычно используется только в многопоточных библиотеках. DLLProc также используется в многопоточных приложениях. Для реализации любого поведения при выходе вместо процедур выхода предпочтительнее использовать секцию финализации.</p><p>Для реализации мониторинга обращений операционной системы следует создавать процедуру обратного вызова, принимающую один целочисленный параметр. Например:</p><pre class="brush:pas">procedure DLLHandler(Reason: Integer);</pre><p>Передайте адрес процедуры в переменную DLLProc. При вызове процедуры она будет передавать одно из следующих значений.</p><table class="dttbl" width="700"><tr><td class="dttd">DLL_PROCESS_DETACH</td><td class="dttd">Указывает, что библиотека выводится из адресного пространства вызывающего процесса в результате корректного завершения работы или вызова FreeLibrary</td></tr>
<tr><td class="dttd">DLL_PROCESS_ATTACH</td><td class="dttd">Указывает, что библиотека вводится в адресное пространство вызывающего процесса в результате вызова LoadLibrary</td></tr>
<tr><td class="dttd">DLL_THREAD_ATTACH</td><td class="dttd">Указывает, что текущий процесс создает новый поток</td></tr>
<tr><td class="dttd">DLL_THREAD_DETACH</td><td class="dttd">Указывает, что выполняется корректное завершение работы потока</td></tr>
</table><p>В теле процедуры можно определить поведение в зависимости от значения этого параметра.</p><h3>Исключения и ошибки выполнения (run time errors) в библиотеках</h3><p>Если при работе библиотеки исключение возникло, но обработчика для него не существует, исключение передается наружу в вызывающий код. Если вызывающее приложение разработано в Delphi, исключение может быть обработано в обычной инструкции <b>try...except</b>.</p><p>На платформе Win32, при вызове приложения или библиотеки, разработанной на другом языке, исключение может быть обработано как исключение операционной системы с кодом $0EEDFADE. Первый элемент в массиве ExceptionInformation, включенном в запись операционной системы об исключении, содержит адрес исключения, а следующий элемент – ссылку на объект исключения Delphi.</p><p>Обычно не следует выводить исключения из библиотеки. Исключения Delphi сопоставляются с моделью исключений операционной системы.</p><p>Если библиотека не подключает модуль SysUtils, поддержка исключений отключается. В этом случае ошибка при выполнении кода библиотеки приводит к аварийному завершению вызывающего приложения. Поскольку библиотека не может определить, вызвана ли она из приложения Delphi, она не может запустить процедуры выхода приложения. По этому оно просто аварийно завершается и удаляется из памяти.</p><h3>Менеджер разделяемой памяти (Shared-Memory Manager)</h3><p>На платформе Win32, если DLL экспортирует подпрограммы, которые передают как параметры или результаты функций длинные строки или динамические массивы (в чистом виде или в составе записей или объектов), DLL и его клиентские приложения (или DLL) должны подключать модуль <b>ShareMem</b>. Это также верно в том случае, когда приложение или DLL выделяет память при помощи New или GetMem, которая освобождается вызовом Dispose или FreeMem в другом модуле. В программе или библиотеке модуль <b>ShareMem</b> всегда должен указываться на первом месте в разделе <b>uses</b>.</p><p><b>ShareMem</b> – это интерфейсный модуль для менеджера памяти BORLANDMM.DLL, который позволяет модулям разделять динамическую память. BORLANDMM.DLL должен быть развернут вместе с приложением и библиотеками, которые подключают <b>ShareMem</b>. Если приложение или библиотека подключает <b>ShareMem</b>, его менеджер памяти заменяется менеджером памяти из BORLANDMM.DLL.</p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-8958570762405441962013-04-12T00:21:00.000+04:002013-04-12T00:50:22.236+04:00Библиотеки и пакеты<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Libraries_and_Packages" target="_blank">Libraries and Packages (Delphi)</a> из справочной системы Delphi</cite><br />
<div align = "justify"><p>Динамически загружаемые библиотеки – это библиотеки динамического связывания (dynamic-link library (DLL)) под Windows или DYLIB на Mac. Они представляют собой подборку подпрограмм, которые могут вызываться приложениями или другими библиотеками. Как и модули, динамически загружаемые библиотеки содержат программный код или ресурсы, предназначенные для совместного использования. Однако этот тип библиотеки компилируется отдельно и связывается с использующей его программой во время ее выполнения.</p><p>Программы, написанные на Delphi, могут вызывать DLL и сборки, написанные на других языках, и приложения, написанные на других языках, могут вызывать DLL или сборки, разработанные в Delphi.</p><a name='more'></a><br />
<h3>Вызов динамически загружаемых библиотек</h3><p>Можно выполнять вызов подпрограмм операционной системы напрямую, но они не будут связаны с вашим приложением до момента его выполнения. Это означает, что во время компиляции библиотека не обязательно должна быть в наличии. Кроме того, при компиляции невозможна проверка корректности импорта подпрограммы. </p><p>Прежде чем вы сможете вызывать подпрограммы, объявленные в DLL или сборке, вы должны импортировать их. Это может быть сделано двумя способами: путем объявления процедуры или функции с указанием директивы <b>external</b> или при помощи прямого обращения к операционной системе. Какой бы способ вы не использовали, подпрограммы не линкуются в приложение до его запуска.</p>Delphi не поддерживает импорт переменных из DLL или сборок.</p><h3>Статическая загрузка</h3><p>Простейший пример импорта процедуры или функции – это объявление ее с директивой <b>external</b>. Например:</p> <pre class="brush:pas">procedure DoSomething; external 'MYLIB.DLL';</pre><p>При включении такого объявления в программу, MYLIB.DLL загружается при запуске программы. Во время выполнения программы, идентификатор DoSomething ссылается на одну и ту же точку входа в библиотеке.</p><p>Объявления импортированных подпрограмм могут располагаться непосредственно в программе или модулях, откуда они вызываются. Для простоты вы можете собрать такие объявления в отдельный "модуль импорта", который также будет содержать константы и типы, необходимые для взаимодействия с библиотекой. Прочие модули, которые подключаются к модулю импорта, смогут вызывать все объявленные в нем подпрограммы.</p><h3>Отложенная загрузка</h3><p>Директиву <b>delayed</b> можно использовать для оформления отсрочки загрузки библиотеки, содержащей подпрограмму. Загрузка начинается только в момент первого обращения. Следующий пример показывает использование директивы <b>delayed</b>:</p><pre class="brush:pas">function GetSomething: Integer; external 'somelibrary.dll' delayed;</pre><p>В этом примере подпрограмма GetSomething импортируется из библиотеки somelibrary.dll. Директива отложенной загрузки гарантирует, что библиотека somelibrary.dll линкуется в приложение динамически, а не статически.</p><p>Директива <b>delayed</b> будет полезна в случае, когда импортируемые подпрограммы могут не существовать в целевой операционной системе, где запускается приложение. Статически импортируемые подпрограммы требуют, чтобы операционная система нашла и загрузила библиотеку при запуске подпрограммы. Если подпрограмма не нашлась в загруженной библиотеке, или библиотека не существует, операционная система аварийно завершает приложение. Использование директивы <b>delayed</b> дает вам возможность при запуске программы проверить, поддерживает ли операционная система затребованные API, и только после этого вызывать импортируемые подпрограммы.</p><p>Еще одно возможное применение директивы <b>delayed</b> связано со следом приложения в памяти: оформление объявления редко используемых подпрограмм с указанием этой директивы может уменьшить след приложения в памяти, поскольку библиотеки загружаются только по требованию. Неверное использование директивы <b>delayed</b> может сказаться на производительности приложения и, в конечном счете, на пользователе.</p><cite>Замечание: Попытка вызова несуществующей подпрограммы, объявленной с директивой <b>delayed</b>, приводит к возникновению ошибки выполнения приложения или к исключительной ситуации (если подключен модуль SysUtils).</cite><br />
<p>Для настройки процесса отложенного вызова подпрограмм, подключаемого в Delphi Run-time Library, вы можете регистрировать процедуры-хуки, контролирующие и изменяющие его поведение. Чтобы сделать это, используйте SetDliNotifyHook2 и SetDliFailureHook2, объявленные в модуле SysInit.</p><h3>Динамическая загрузка (только для Windows)</h3><p>Вы можете получить доступ к библиотеке при прямом обращении к Windows API при помощи LoadLibrary, FreeLibrary иGetProcAddress. Эти функции объявлены в модуле Windows.pas. В этом случае следует пользоваться процедурными переменными для обращения к импортированным подпрограммам. Например:</p><pre class="brush:pas"> uses Windows, ...;
type
TTimeRec = record
Second: Integer;
Minute: Integer;
Hour: Integer;
end;
TGetTime = procedure(var Time: TTimeRec);
THandle = Integer;
var
Time: TTimeRec;
Handle: THandle;
GetTime: TGetTime;
.
.
.
begin
Handle := LoadLibrary('libraryname');
if Handle <> 0 then
begin
@GetTime := GetProcAddress(Handle, 'GetTime');
if @GetTime <> nil then
begin
GetTime(Time);
with Time do
Writeln('The time is ', Hour, ':', Minute, ':', Second);
end;
FreeLibrary(Handle);
end;
end;</pre><p>Если вы импортируете подпрограммы этим способом, библиотека не загружается до момента выполнения кода, содержащего LoadLibrary. Позже библиотека выгружается вызовом FreeLibrary. Такой способ позволит вам сберечь память и запускать программу, даже когда некоторые используемые ею библиотеки отсутствуют.</p><br />
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-12674268088321094662013-03-25T04:35:00.000+04:002013-03-27T00:57:14.237+04:00Ввод/вывод. Стандартные подпрограммы<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Standard_Routines_and_Input-Output" target="_blank">Standard Routines and Input-Output</a> из справочной системы Delphi</cite><br/><br/><br />
<div align = "justify"><p>В этой теме обсуждается текстовый и файловый ввод/вывод, приводится обзор списка функций стандартных библиотек. Большая часть перечисленных здесь процедур и функций объявлена в модулях System и SysInit, которые автоматически подключаются во всех приложениях. Другие функции встроены в компилятор и обрабатываются так, как будто они присутствуют в модуле System.</p><p>Некоторые стандартные подпрограммы включены в такие модули как SysUtils, и для доступа к ним из ваших программ необходимо включить имена модулей в раздел <b>uses</b>. Вы не должны ни включать модуль System в раздел uses, ни изменять его программный код или пытаться перекомпилировать его.</p><a name='more'></a><br/><br />
<p><b> Замечание.</b> Для новых программ вы возможно захотите использовать классы и функции для работы с файлами, которые объявлены в модулях <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes" title="lib en:System.Classes">System.Classes</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils" title="lib en:System.SysUtils">System.SysUtils</a>. Для работы с файлами в Delphi на текущий момент рекомендуется использовать Класс <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes.TStream" title="lib en:System.Classes.TStream">Classes.TStream</a> и его предков (чтобы получить информацию по их подпрограммам см. <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/Streams,_Reader_and_Writers" title="Streams, Reader and Writers">Streams, Reader and Writers</a>). Для работы с текстовыми файлами рекомендуется использовать <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes.TStreamReader" title="lib en:System.Classes.TStreamReader">TStreamReader</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes.TStreamWriter" title="lib en:System.Classes.TStreamWriter">TStreamWriter</a>, а не <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Write" title="lib en:System.Write">Write</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Writeln" title="lib en:System.Writeln">Writeln</a>. <a href="http://docwiki.embarcadero.com/RADStudio/XE3/en/API_Categories_Index" title="API Categories Index">API Categories Index</a> содержит список подпрограмм и классов по этим вопросам.</p><p><b>Замечание.</b> <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.BlockRead" title="lib en:System.BlockRead">BlockRead</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.BlockWrite" title="lib en:System.BlockWrite">BlockWrite</a> имеют нетипизированные параметры, которые могут явиться причиной ошибок в работе с памятью. Оба метода зависят от размера записи, установленном неявно при предыдущем вызове Reset или Rewrite. Применение потоков дает программисту более высокий уровень гибкости и функциональности. </p><br><br><br />
<h2 >Работа с файлами</h2><p>Далее в таблице перечислены подпрограммы ввода/вывода.</p><table class="dttbl" width="700"><tr><th class="dtth">Процедура или функция</th><th class="dtth">Описание</th></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Append" title="lib en:System.Append">Append</a></td>
<td class="dttd">Открывает существующий файл для добавления.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.AssignFile" title="lib en:System.AssignFile">AssignFile</a></td>
<td class="dttd">Связывает имя внешнего файла с файловой переменной </td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.BlockRead" title="lib en:System.BlockRead">BlockRead</a> </td>
<td class="dttd">Читает одну или несколько записей из нетипизированного файла.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.BlockWrite" title="lib en:System.BlockWrite">BlockWrite</a> </td>
<td class="dttd">Записывает одну или несколько записей в нетипизированный файл.</td></tr>
<tr><td class="dttd"> <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.ChDir" title="lib en:System.ChDir">ChDir</a></td>
<td class="dttd">Изменяет текущий директорий.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.CloseFile" title="lib en:System.CloseFile">CloseFile</a> </td>
<td class="dttd">Закрывает открытый файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Eof"title="lib en:System.Eof">Eof</a></td>
<td class="dttd">Возвращает признак конца файла.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Eoln"title="lib en:System.Eoln">Eoln</a></td>
<td class="dttd">Возвращает признак конца строки текстового файла.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Erase"title="lib en:System.Erase">Erase</a></td>
<td class="dttd">Стирает внешний файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.FilePos"title="lib en:System.FilePos">FilePos</a></td>
<td class="dttd">Возвращает текущую позицию в нетипизированном файле.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.FileSize"title="lib en:System.FileSize">FileSize</a></td>
<td class="dttd">Возвращает текущий размер файла. Не применяется с текстовыми файлами.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Flush"title="lib en:System.Flush">Flush</a></td>
<td class="dttd">Очищает буфер при записи текстового файла.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.GetDir"title="lib en:System.GetDir">GetDir</a></td>
<td class="dttd">Возвращает текущий директорий на определенном диске.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.IOResult"title="lib en:System.IOResult">IOResult</a></td>
<td class="dttd">Возвращает целочисленное значение, которое является статусом последней выполненной операции ввода/вывода.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.MkDir"title="lib en:System.MkDir">MkDir</a></td>
<td class="dttd">Создает вложенный директорий.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Read"title="lib en:System.Read">Read</a></td>
<td class="dttd">Считывает одно или несколько значений из файла в одну или несколько переменных.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Readln"title="lib en:System.Readln">Readln</a></td>
<td class="dttd">Делает тоже самое, что и Read, затем переходит к началу следующей строки в текстовом файле.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Rename"title="lib en:System.Rename">Rename</a></td>
<td class="dttd">Переименовывает внешний файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Reset"title="lib en:System.Reset">Reset</a></td>
<td class="dttd">Открывает существующий файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Rewrite"title="lib en:System.Rewrite">Rewrite</a></td>
<td class="dttd">Создает и открывает новый файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.RmDir"title="lib en:System.RmDir">RmDir</a></td>
<td class="dttd">Удаляет пустой поддиректорий.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Seek"title="lib en:System.Seek">Seek</a></td>
<td class="dttd">Перемещает текущую позицию в типизированном или нетипизированном файле определенное место. Не используется с текстовыми файлами.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SeekEof"title="lib en:System.SeekEof">SeekEof</a></td>
<td class="dttd">Возвращает признак конца файла в текстовом файле.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SeekEoln"title="lib en:System.SeekEoln">SeekEoln</a></td>
<td class="dttd">Возвращает признак конца строки в текстовом файле.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SetTextBuf"title="lib en:System.SetTextBuf">SetTextBuf</a></td>
<td class="dttd">Связывает буфер ввода/вывода с текстовым файлом.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Truncate"title="lib en:System.Truncate">Truncate</a></td>
<td class="dttd">Обрезает типизированный или нетипизированный файл до текущей позиции.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Write"title="lib en:System.Write">Write</a></td>
<td class="dttd">Записывает одно или несколько значений в файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Writeln"title="lib en:System.Writeln">Writeln</a></td>
<td class="dttd">Делает тоже самое, что и Write, затем записывает маркер конца строки в текстовый файл.</td></tr>
</table><p>Файловая переменная – это переменная типа файл. Есть три вида файлов: типизированные, текстовые и нетипизированные. Синтаксис объявления файловых типов дан в разделе Файловые типы. Следует помнить, что файловые типы доступны только при работе с платформой Win32. </p><p>Перед использованием файловой переменной она должна быть связана с внешним файлов при помощи вызова процедуры AsignFile. Внешний файл – это обычно файл на диске с определенным именем, но кроме того, он может быть и устройством, например клавиатурой или монитором. Внешний файл хранит информацию, записанную в файл, или обеспечивает чтение из файла.</p><p>После того как связь с внешним файлом установлена, для подготовки ввода/вывода файловая переменная должна быть открыта. Существующий файл может быть открыт процедурой Reset, а новый файл создается и открывается процедурой Rewrite. Текстовые файлы, открытые при помощи Reset, доступны только для чтения. Текстовые файлы, открытые при помощи Rewrite и Append, доступны только для записи. Типизированные и нетипизированные файлы допускают и чтение, и запись, вне зависимости от того, были ли они открыты Reset или Rewrite. </p><p>Все файлы представляют собой линейную последовательность компонентов, каждый из которых имеет тип (тип компонента или тип запись), соответствующий типу файла. Компоненты пронумерованы, отсчет начинается с нуля.</p><p>Обычно доступ к содержимому файлов осуществляется последовательно. То есть, после того как компонент считан процедурой Read или записан процедурой Write, указатель текущей позиции в файле перемещается к следующему компоненту. Доступ к типизированным и нетипизированным файлам может также выполняться в произвольном порядке при помощи процедуры Seek, перемещающей указатель текущей позиции к указанному компоненту. Стандартные функции FilePos и FileSize используются для определения текущей позиции и текущего размера файла. </p><p>Когда программа завершает обработку файла, он должен быть закрыт при помощи стандартной процедуры CloseFile. После того как файл закрыт, связанный внешний файл обновляется. После этого файловая переменная может быть связана с другим внешним файлом. </p><p>По умолчанию все вызовы стандартных процедур и функций ввода/вывода автоматически проверяются на ошибки, и при возникновении ошибки инициируется исключение (или работа программы завершается, если не включен режим обработки исключений). Эта автоматическая проверка может быть включена или выключена при помощи директив компилятора <b>{$</b><b>I</b><b>+}</b> и <b>{$</b><b>I</b><b>-}. </b>Когда проверка ввода/вывода выключена, то есть программа или функция компилируется в режиме <b>{$</b><b>I</b><b>-},</b> ошибка ввода/вывода не приводит к инициированию исключения, а для проверки результата операции ввода/вывода вам необходимо вызвать стандартную функцию IOResult. </p><p>Вам необходимо вызывать функцию IOResult для очистки ошибки, даже если вам не нужна информация о ней. Если очистки ошибки не происходит и текущим режимом является <b>{$</b><b>I</b><b>-},</b> следующий вызов функций ввода/вывода завершается предыдущей ошибкой IOResult.</p><br><br><h3 >Текстовые файлы</h3><p>В этом разделе обобщается информация по вводу/выводу с использованием файловых переменных стандартного типа Text.</p><p>При открытии текстового файла, внешний файл интерпретируется особым образом: он рассматривается как последовательность символов, отформатированных в строки, каждая из которых завершается маркером конца строки (символ возврата каретки, за которым может следовать символ перевода строки). Тип текст отличается от типа file of Char.</p><p>Для текстовых файлов существуют специальные формы процедур Read и Write, которые позволяют считывать значения, не являющиеся символьным типом. Такие значения автоматически интерпретируются в их символьном представлении. Например, инструкция Read(F, I)(где I – это целочисленная переменная) считывает последовательность цифр, интерпретирует эту последовательность как десятичное число и сохраняет его в I.</p><p>Существует две стандартные переменные типа текстовый файл - <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Input" title="lib en:System.Input">System.Input</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Output" title="lib en:System.Output">System.Output</a> . Стандартная переменная <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Input" title="lib en:System.Input">System.Input</a> – это файл только для чтения, связанный со стандартным устройством ввода операционной системы (обычно это клавиатура). Стандартная файловая переменная <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Output" title="lib en:System.Output">System.Output</a> – это файл только для записи, связанный со стандартным устройством вывода операционной системы (обычно это дисплей). Перед началом выполнения приложения <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Input" title="lib en:System.Input">System.Input</a> и <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Output" title="lib en:System.Output">System.Output</a> открываются в автоматическом режиме, как при выполнении следующих инструкций:</p><pre class="brush:pas">AssignFile(Input, '');
Reset(Input);
AssignFile(Output, '');
Rewrite(Output);</pre><p><b>Замечание:</b> Для приложений Win32 тексто-ориентированный ввод/вывод возможен только в консольных приложениях, то есть приложениях, скомпилированных с опцией <b>Generate console application</b> на странице Linking в диалоговом окне опций проекта или с опцией командной строки компилятора <b>-</b><b>cc</b>. В приложениях графического пользовательского интерфейса (не консольных) любая попытка чтения или записи при помощи <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Input" title="lib en:System.Input">System.Input</a> или <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Output" title="lib en:System.Output">System.Output</a> вызовет ошибку ввода/вывода.</p><p>Некоторые стандартные подпрограммы ввода/вывода, работающие с текстовыми файлами, не нуждаются в явном указании файловой переменной в качестве параметра. Если файловый параметр опущен, <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Input" title="lib en:System.Input">System.Input</a> или <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Output" title="lib en:System.Output">System.Output</a> принимаются за умолчание, в зависимости от того, ориентирована подпрограмма на ввод или вывод. Например, вызов Read(X)соответствует Read(Input, X), а Write(X) – соответствует вызову Write(Output, X).</p><p>Если при вызове подпрограмм ввода/вывода, работающих с текстовыми файлами, вы указываете файл, он должен быть связан с внешним файлом при помощи AssignFile и открыт при помощи Reset, Rewrite или Append. При передаче в подпрограмму вывода параметра-файла, открытого при помощи Reset, происходит ошибка. Ошибка случится также, если вы передаете в подпрограмму ввода файл, открытый при помощи Rewrite или Append.</p><br><br><h3 >Нетипизированные файлы</h3><p>Нетипизированные файлы представляют собой низкоуровневые каналы ввода/вывода, которые чаще всего используют для прямого доступа к файлам на диске без учета их типа и структуры. Нетипизированные файлы объявляются при помощи ключевого слова file. Например:</p><pre class="brush:pas">varDataFile: file;</pre><p>При работе с нетипизированными файлам в процедурах Reset и Rewrite разрешается указывать дополнительный параметр для определения размера записи, используемого при передаче данных. По историческим причинам размер записи по умолчанию равен 128 байтам. Размер записи равный 1 – это единственное значение, которое правильно отражает размерность любого файла (поскольку в этом случае нельзя прочитать запись частично).</p><p>При работе с нетипизированными файлами разрешен вызов любых стандартных подпрограмм, кроме Read и Write. Вместо них следует использовать две процедуры - BlockRead и BlockWrite, который применяются для высокоскоростной передаче данных.</p><br><br><h2 >Драйверы устройств для текстовых файлов</h2><p>В ваших программах вы можете определить собственные драйверы устройств для текстовых файлов. Драйвер устройства для текстового файла – это набор из четырех функций, полностью реализующих интерфейс между файловой системой Delphi и некоторым устройством. </p><p>Драйвер устройства определяют четыре функции:Open, InOut, Flush и Close. Заголовок функции для каждой из них выглядит следующим образом:</p><pre class="brush:pas">function DeviceFunc(var F: TTextRec): Integer;</pre><p>где DeviceFunc – это название функции (то есть Open, InOut, Flush или Close). Значением, которое возвращают эти функции, становится результат, возвращаемый IOResult. Операция выполнена успешно, если возвращаемое значение равно нулю.</p><p>Чтобы связать функции интерфейса драйвера с определенным файлом, вам необходимо переписать процедуру Assign. Процедура Assign должна назначить адреса четырех функций интерфейса устройства четырем указателям на функции в переменной текстового файла. Кроме того она должна записать волшебную константу fmClosed в поле Mode, записать размер буфера текстового файла в BufSize,записать указатель на буфер текстового файла вBufPtr и очистить строку Name.</p><p>Для примера предположим, что четыре функции интерфейса драйвера называются DevOpen, DevInOut, DevFlush и DevClose, процедура Assign могла бы выглядеть следующим образом:</p><pre class="brush:pas">procedure AssignDev(var F: Text);
begin
with TTextRec(F) do
begin
Mode := fmClosed;
BufSize := SizeOf(Buffer);
BufPtr := @Buffer;
OpenFunc := @DevOpen;
InOutFunc := @DevInOut;
FlushFunc := @DevFlush;
CloseFunc := @DevClose;
Name[0] := #0;
end;
end;</pre><p>Функции интерфейса устройства могут использовать поле UserData в записи файла для хранения частной информации. Это поле никогда не изменяется файловой системой продукта.</p><br><br><h3 >Функция Open</h3><p>Функция Open вызывается стандартными процедурами Reset, Rewrite и Append для открытия текстового файла, связанного с устройством. При запуске функции константы поля Mode <i>fmInput</i>, <i>fmOutput</i><i> </i>или <i>fmInOut</i> определяют, была ли функция Open вызвана из Reset, Rewrite или из Append.</p><p>В зависимости от значения Mode функция Open подготавливает файл для ввода или вывода. Если Mode имеет значение <i>fmInOut</i> (это означает, что вызов был выполнен из Append), то до завершения работы функции Open оно должно быть установлено в <i>fmOutput</i>.</p><p>Функция Open всегда вызывается перед запуском остальных функций интерфейса устройства. Поэтому AssignDev только инициализирует поле OpenFunc, откладывая инициализацию остальных векторов до выполнения Open. После этого, в зависимости от значения Mode, функция Open устанавливает указатели либо на функции ввода, либо на функции ввода. По этой причине в подпрограммах InOut, Flush и CloseFile нет необходимости определять текущий режим работы.</p><br><br><h3 >Функция InOut</h3><p>Функция InOut вызывается стандартными подпрограммами Read, Readln, Write, Writeln, Eof, Eoln, SeekEof, SeekEoln и CloseFile в случае, когда необходим ввод или вывод из устройства.</p><p>Когда значение Mode установлено в <i>fmInput</i>, функция InOut считывает символы в количестве, определяемом BufSize, в BufPtr^, и возвращает количество считанных символов в BufEnd. Кроме того, она записывает в BufPos значение 0. Если в результате запроса ввода функция InOut возвращает 0 в BufEnd, Eof для этого файла становится равным <b>True</b>.</p><p>Когда Mode имеет значение <i>fmOutput</i>, функция InOut записывает символы изBufPtr^, количество которых определяется значением BufPos, и затем возвращает 0 в BufPos.</p><br><br><h3 >Функция function</h3><p>Функция Flush вызывается при завершении каждого вызова Read, Readln, Write и Writeln. Опционально она может очищать буфер текстового файла.</p><p>Если значение Mode установлено в <i>fmInput</i>, функция Flush может сохранить значение 0 в BufPos и BufEnd чтобы удалить оставшиеся (непрочитанные) символы из буфера, однако эта функциональность используется редко.</p><p>Если Mode имеет значение <i>fmOutput</i>, функция Flush может заполнить содержимое буфера так же, как это происходит при вызове функции InOut, это позволяет быть уверенным, что текст, записываемый в устройство, появится там немедленно. Если Flush не выполнит никаких действий, текст не появится в устройстве до момента заполнения буфера или закрытия файла.</p><br><br><h3 >Функция Close</h3><p>Функция Close вызывается стандартной процедурой CloseFile для закрытия текстового файла, связанного с устройством. (Процедуры Reset, Rewrite и Append тоже вызывают Close, в том случае, когда файл, который они открывают, уже открыт.) Если Mode имеет значение <i>fmOutput</i>, перед вызовом Close, файловая система вызывает функцию InOut чтобы гарантировать, что все символы записаны в устройство.</p><br><br><h2 >Обработка строк, завершающихся нулевым символом</h2><p>Расширенный синтаксис языка Delphi позволяет стандартным процедурам Read, Readln, Str и Val работать с индексируемыми от нуля массивами символов, а стандартным процедурам Write, Writeln, Val, AssignFile и Rename работать как индексируемыми от нуля символьными массивами, так и с символьными указателями.</p><br><br><h3 >Функции для работы со строками, завершающимися нулевым символом</h3><p>Следующие функции предназначены для обработки строк, завершающихся нулевым символом.</p><table class="dttbl" width="700"><tr><th class="dtth">Функция</th><th class="dtth">Описание</th></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrAlloc"title="lib en:System.SysUtils.StrAlloc">StrAlloc</a></td>
<td class="dttd">Создает символьный буфер заданного размера в куче.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrBufSize"title="lib en:System.SysUtils.StrBufSize">StrBufSize</a></td>
<td class="dttd">Возвращает размер символьного буфера, созданного при помощи StrAlloc или StrNew.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrCat"title="lib en:System.SysUtils.StrCat">StrCat</a></td>
<td class="dttd">Сцепляет две строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrComp"title="lib en:System.SysUtils.StrComp">StrComp</a></td>
<td class="dttd">Сравнивает две строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrCopy"title="lib en:System.SysUtils.StrCopy">StrCopy</a></td>
<td class="dttd">Копирует строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrDispose"title="lib en:System.SysUtils.StrDispose">StrDispose</a></td>
<td class="dttd">Ликвидирует символьный буфер, созданный при помощи StrAlloc или StrNew.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrECopy"title="lib en:System.SysUtils.StrECopy">StrECopy</a></td>
<td class="dttd">Копирует строку и возвращает указатель на конец строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrEnd"title="lib en:System.SysUtils.StrEnd">StrEnd</a></td>
<td class="dttd">Возвращает указатель на конец строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrFmt"title="lib en:System.SysUtils.StrFmt">StrFmt</a></td>
<td class="dttd">Форматирует одно или несколько значений в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrIComp"title="lib en:System.SysUtils.StrIComp">StrIComp</a></td>
<td class="dttd">Сравнивает две строки без учета регистра символов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLCat"title="lib en:System.SysUtils.StrLCat">StrLCat</a></td>
<td class="dttd">Сцепляет две строки в строку с заданной максимально длиной.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLComp"title="lib en:System.SysUtils.StrLComp">StrLComp</a></td>
<td class="dttd">Сравнивает две строки по фрагменту заданной длины.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLCopy"title="lib en:System.SysUtils.StrLCopy">StrLCopy</a></td>
<td class="dttd">Копирует часть строки заданного размера.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLen"title="lib en:System.SysUtils.StrLen">StrLen</a></td>
<td class="dttd">Возвращает длину строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLFmt"title="lib en:System.SysUtils.StrLFmt">StrLFmt</a></td>
<td class="dttd">Форматирует одно или несколько значений в строку заданного размера.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLIComp"title="lib en:System.SysUtils.StrLIComp">StrLIComp</a></td>
<td class="dttd">Сравнивает две строки по фрагменту заданной длины без учета регистра символов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrLower"title="lib en:System.SysUtils.StrLower">StrLower</a></td>
<td class="dttd">Преобразует символы в строке в строчные (нижний регистр).</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrMove"title="lib en:System.SysUtils.StrMove">StrMove</a></td>
<td class="dttd">Перемещает символы из одной строки в другую.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrNew"title="lib en:System.SysUtils.StrNew">StrNew</a></td>
<td class="dttd">Создает строку в куче.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPCopy"title="lib en:System.SysUtils.StrPCopy">StrPCopy</a></td>
<td class="dttd">Копирует паскалевскую строку в строку, завершающуюся нулевым символом.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPLCopy"title="lib en:System.SysUtils.StrPLCopy">StrPLCopy</a></td>
<td class="dttd">Копирует фрагмент паскалевской строки заданного размера в строку, завершающуюся нулевым символом.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrPos"title="lib en:System.SysUtils.StrPos">StrPos</a></td>
<td class="dttd">Возвращает указатель на первое вхождение заданной подстроки в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrRScan"title="lib en:System.SysUtils.StrRScan">StrRscan</a></td>
<td class="dttd">Возвращает указатель на последнее вхождение заданного символа в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrScan"title="lib en:System.SysUtils.StrScan">StrScan</a></td>
<td class="dttd">Возвращает указатель на первое вхождение заданного символа в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrUpper"title="lib en:System.SysUtils.StrUpper">StrUpper</a></td>
<td class="dttd">Преобразует символы в строке в верхний регистр.</td></tr>
</table><p>У стандартных функций для обработки строк есть мультибайтовые коллеги, поддерживающие и работу с символами, специфичными для локали. Названия мультибайтовых функций начинаются с префикса Ansi-. Например, мультибайтовая версия StrPos – это AnsiStrPos. Поддержка мультибайтовых символов зависит от операционной системы и основывается на текущей локали.</p><br><br><h3 >Динамические строки</h3><p>Модуль System содержит три функции (WideCharToString, WideCharLenToString и StringToWideChar), которые могут быть использованы для преобразования динамических строк, завершающихся нулевым символом в строки с однобайтовой и двухбайтовой длиной.</p><p>Присваивание будет также выполнять преобразование строк. То есть оба следующих примера являются правильными:</p><pre class="brush:pas">MyAnsiString := MyWideString;
MyWideString := MyAnsiString;</pre><br><br><h2 >Прочие стандартные подпрограммы</h2><p>Ниже в таблице указаны часто используемые процедуры и функции, которые можно найти в библиотеках, поставляемых с продуктом. Этот перечень стандартных подпрограмм не является полным.</p><table class="dttbl" width="700"><tr><th class="dtth">Процедура или функция</th> <th class="dtth">Описание</th></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Addr"title="lib en:System.Addr">Addr</a></td>
<td class="dttd">Возвращает указатель на заданный объект.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.AllocMem"title="lib en:System.AllocMem">AllocMem</a></td>
<td class="dttd">Выделяет блок памяти и инициализирует его байты нолями.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.ArcTan"title="lib en:System.ArcTan">ArcTan</a></td>
<td class="dttd">Вычисляет арктангенс заданного числа.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Assert"title="lib en:System.Assert">Assert</a></td>
<td class="dttd">Создает исключительную ситуацию, если значение передаваемого как параметр выражения не является истиной.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Assigned"title="lib en:System.Assigned">Assigned</a></td>
<td class="dttd">Проверяет значение указателя или процедурной переменной на nil.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Beep"title="lib en:System.SysUtils.Beep">Beep</a></td>
<td class="dttd">Подает стандартный сигнал.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Break"title="lib en:System.Break">Break</a></td>
<td class="dttd">Выполняет выход из инструкций <b>for</b>, <b>while</b>, или <b>repeat</b>.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.ByteToCharIndex"title="lib en:System.SysUtils.ByteToCharIndex">ByteToCharIndex</a></td>
<td class="dttd">Возвращает позицию символа, содержащего определенный байт, в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Chr"title="lib en:System.Chr">Chr</a></td>
<td class="dttd">Возвращает символ для заданного целочисленного значения.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Close"title="lib en:System.Close">Close</a></td>
<td class="dttd">Закрывает файл.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.CompareMem"title="lib en:System.SysUtils.CompareMem">CompareMem</a></td>
<td class="dttd">Выполняет бинарное сравнение двух фрагментов памяти.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.CompareStr"title="lib en:System.SysUtils.CompareStr">CompareStr</a></td>
<td class="dttd">Сравнивает строки с учетом регистра.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.CompareText"title="lib en:System.SysUtils.CompareText">CompareText</a></td>
<td class="dttd">Сравнивает строки по порядковым значениям символов без учета регистра.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Continue"title="lib en:System.Continue">Continue</a></td>
<td class="dttd">Начинает новую итерацию в инструкциях <b>for</b>, <b>while</b><b> </b>или <b>repeat</b>.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Copy"title="lib en:System.Copy">Copy</a></td>
<td class="dttd">Возвращает подстроку из строки или сегмента динамического массива.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Cos"title="lib en:System.Cos">Cos</a></td>
<td class="dttd">Вычисляет косинус угла.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.CurrToStr"title="lib en:System.SysUtils.CurrToStr">CurrToStr</a></td>
<td class="dttd">Преобразует значение типа currency в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Date"title="lib en:System.SysUtils.Date">Date</a></td>
<td class="dttd">Возвращает текущую дату.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.DateTimeToStr"title="lib en:System.SysUtils.DateTimeToStr">DateTimeToStr</a></td>
<td class="dttd">Преобразует переменную типа TDateTime в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.DateToStr"title="lib en:System.SysUtils.DateToStr">DateToStr</a></td>
<td class="dttd">Преобразует значение типа TDateTime в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Dec"title="lib en:System.Dec">Dec</a></td>
<td class="dttd">Уменьшает значение переменной порядкового типа или значение типизированного указателя.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Dispose"title="lib en:System.Dispose">Dispose</a></td>
<td class="dttd">Освобождает динамически выделенную память переменной.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.ExceptAddr"title="lib en:System.ExceptAddr">ExceptAddr</a></td>
<td class="dttd">Возвращает адрес, по которому возникла текущая исключительная ситуация.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Exit"title="lib en:System.Exit">Exit</a></td>
<td class="dttd">Выполняет выход из текущей процедуры.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Exp"title="lib en:System.Exp">Exp</a></td>
<td class="dttd">Вычисляет экспоненту X.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.FillChar"title="lib en:System.FillChar">FillChar</a></td>
<td class="dttd">Заполняет смежные байты заданным значением.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Finalize"title="lib en:System.Finalize">Finalize</a></td>
<td class="dttd">Финализирует динамически созданную переменную.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FloatToStr"title="lib en:System.SysUtils.FloatToStr">FloatToStr</a></td>
<td class="dttd">Преобразует число с плавающей точкой в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FloatToStrF"title="lib en:System.SysUtils.FloatToStrF">FloatToStrF</a></td>
<td class="dttd">Преобразует число с плавающей точкой в строку с использованием заданного форматирования.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FmtLoadStr"title="lib en:System.SysUtils.FmtLoadStr">FmtLoadStr</a></td>
<td class="dttd">Возвращает отформатированную строку, полученную из ресурсов программы.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FmtStr"title="lib en:System.SysUtils.FmtStr">FmtStr</a></td>
<td class="dttd">Собирает форматированную строку из массива аргументов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Format"title="lib en:System.SysUtils.Format">Format</a></td>
<td class="dttd">Собирает форматированную строку из массива аргументов. </td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FormatDateTime"title="lib en:System.SysUtils.FormatDateTime">FormatDateTime</a></td>
<td class="dttd">Форматирует значение типа TDateTime.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.FormatFloat"title="lib en:System.SysUtils.FormatFloat">FormatFloat</a></td>
<td class="dttd">Форматирует число с плавающей точкой.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.FreeMem"title="lib en:System.FreeMem">FreeMem</a></td>
<td class="dttd">Освобождает выделенную память.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.GetMem"title="lib en:System.GetMem">GetMem</a></td>
<td class="dttd">Выделяет динамическую память и возвращает указатель на выделенную</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Halt"title="lib en:System.Halt">Halt</a></td>
<td class="dttd">Инициирует аварийное завершение работы программы.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Hi"title="lib en:System.Hi">Hi</a></td>
<td class="dttd">Возвращает старший байт выражения как положительное целое число.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.High"title="lib en:System.High">High</a></td>
<td class="dttd">Возвращает верхнюю границу перечисляемого типа, массива или строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Inc"title="lib en:System.Inc">Inc</a></td>
<td class="dttd">Увеличивает значение переменной перечисляемого типа или типизированного указателя.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Initialize"title="lib en:System.Initialize">Initialize</a></td>
<td class="dttd">Инициализирует динамическую переменную.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Insert"title="lib en:System.Insert">Insert</a></td>
<td class="dttd">Вставляет подстроку в указанную позицию в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Int"title="lib en:System.Int">Int</a></td>
<td class="dttd">Возвращает целочисленную часть дробного числа.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.IntToStr"title="lib en:System.SysUtils.IntToStr">IntToStr</a></td>
<td class="dttd">Преобразует целое число в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Length"title="lib en:System.Length">Length</a></td>
<td class="dttd">Возвращает длину строки или массива.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Lo"title="lib en:System.Lo">Lo</a></td>
<td class="dttd">Возвращает младший байт выражения как положительное целое число.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Low"title="lib en:System.Low">Low</a></td>
<td class="dttd">Возвращает верхнюю границу перечисляемого типа, массива или строки.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.LowerCase"title="lib en:System.SysUtils.LowerCase">Lowercase</a></td>
<td class="dttd">Преобразует строку ASCII в нижний регистр.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Math.MaxIntValue"title="lib en:System.Math.MaxIntValue">MaxIntValue</a></td>
<td class="dttd">Возвращает максимальное значение в массиве целых чисел.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Math.MaxValue"title="lib en:System.Math.MaxValue">MaxValue</a></td>
<td class="dttd">Возвращает максимальное значение в массиве.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Math.MinIntValue"title="lib en:System.Math.MinIntValue">MinIntValue</a></td>
<td class="dttd">Возвращает минимальное значение в массиве целых чисел.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Math.MinValue"title="lib en:System.Math.MinValue">MinValue</a></td>
<td class="dttd">Возвращает максимальное значение в массиве.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.New"title="lib en:System.New">New</a></td>
<td class="dttd">Создает динамическую переменную и связывает с ней указатель.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Now"title="lib en:System.SysUtils.Now">Now</a></td>
<td class="dttd">Возвращает текущую дату и время.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Ord"title="lib en:System.Ord">Ord</a></td>
<td class="dttd">Возвращает порядок значения перечисляемого типа.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Pos"title="lib en:System.Pos">Pos</a></td>
<td class="dttd">Возвращает позицию первого вхождения однобайтового символа указанной подстроки в строке.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Pred"title="lib en:System.Pred">Pred</a></td>
<td class="dttd">Возвращает значение, предшествующее заданному порядковому значению.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Ptr"title="lib en:System.Ptr">Ptr</a></td>
<td class="dttd">Преобразует значение в указатель.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Random"title="lib en:System.Random">Random</a></td>
<td class="dttd">Генерирует случайные числа в заданном диапазоне.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.ReallocMem"title="lib en:System.ReallocMem">ReallocMem</a></td>
<td class="dttd">Reallocates a dynamically allocatable memory.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Round"title="lib en:System.Round">Round</a></td>
<td class="dttd">Возвращает значение дробного числа, округленное до ближайшего целого.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SetLength"title="lib en:System.SetLength">SetLength</a></td>
<td class="dttd">Устанавливает длину строки или динамического массива.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SetString"title="lib en:System.SetString">SetString</a></td>
<td class="dttd">Задает содержимое строки и ее длину.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.ShowException"title="lib en:System.SysUtils.ShowException">ShowException</a></td>
<td class="dttd">Выводит сообщение об исключении и его адрес.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Dialogs.ShowMessage"title="lib en:Vcl.Dialogs.ShowMessage">ShowMessage</a></td>
<td class="dttd">Выводит окно с неформатированным сообщением и кнопкой ОК.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Dialogs.ShowMessageFmt"title="lib en:Vcl.Dialogs.ShowMessageFmt">ShowMessageFmt</a></td>
<td class="dttd">Выводит окно с форматированным сообщением и кнопкой ОК.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Sin"title="lib en:System.Sin">sin</a></td>
<td class="dttd">Возвращает синус угла в радианах.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SizeOf"title="lib en:System.SizeOf">SizeOf</a></td>
<td class="dttd">Возвращает количество байт, требуемое для хранения переменной или типа.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Slice"title="lib en:System.Slice">Slice</a></td>
<td class="dttd">Возвращает фрагмент массива.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Sqr"title="lib en:System.Sqr">Sqr</a></td>
<td class="dttd">Возвращает число, возведенное в квадрат.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Sqrt"title="lib en:System.Sqrt">Sqrt</a></td>
<td class="dttd">Возвращает квадратный корень числа.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Str"title="lib en:System.Str">Str</a></td>
<td class="dttd">Преобразует целое или дробное число в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToCurr"title="lib en:System.SysUtils.StrToCurr">StrToCurr</a></td>
<td class="dttd">Преобразует строку в значение типа currency.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToDate"title="lib en:System.SysUtils.StrToDate">StrToDate</a></td>
<td class="dttd">Преобразует строку в дату формата TDateTime.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToDateTime"title="lib en:System.SysUtils.StrToDateTime">StrToDateTime</a></td>
<td class="dttd">Преобразует строку в дату и время (TDateTime).</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToFloat"title="lib en:System.SysUtils.StrToFloat">StrToFloat</a></td>
<td class="dttd">Преобразует строку в число с плавающей точкой.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToInt"title="lib en:System.SysUtils.StrToInt">StrToInt</a></td>
<td class="dttd">Преобразует строку в целое число.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrToTime"title="lib en:System.SysUtils.StrToTime">StrToTime</a></td>
<td class="dttd">Преобразует строку во время (TDateTime).</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.StrUpper"title="lib en:System.SysUtils.StrUpper">StrUpper</a></td>
<td class="dttd">Возвращает строку ASCII в верхнем регистре.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Succ"title="lib en:System.Succ">Succ</a></td>
<td class="dttd">Возвращает значение, следующее за заданным порядковым значением.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Math.Sum"title="lib en:System.Math.Sum">Sum</a></td>
<td class="dttd">Возвращает сумму элементов массива.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Time"title="lib en:System.SysUtils.Time">Time</a></td>
<td class="dttd">Возвращает текущее время.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.TimeToStr"title="lib en:System.SysUtils.TimeToStr">TimeToStr</a></td>
<td class="dttd">Преобразует значение типа TDateTime в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Trunc"title="lib en:System.Trunc">Trunc</a></td>
<td class="dttd">Обрезает вещественное число до целого.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.UniqueString"title="lib en:System.UniqueString">UniqueString</a></td>
<td class="dttd">Обеспечивает уникальность ссылки на строку. (Строка может быть скопирована с уникальной ссылкой).</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.UpCase"title="lib en:System.UpCase">Upcase</a></td>
<td class="dttd">Преобразует символ в верхний регистр.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.UpperCase"title="lib en:System.SysUtils.UpperCase">UpperCase</a></td>
<td class="dttd">Возвращает строку в верхнем регистре.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayCreate"title="lib en:System.Variants.VarArrayCreate">VarArrayCreate</a></td>
<td class="dttd">Создает массив значений типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayDimCount"title="lib en:System.Variants.VarArrayDimCount">VarArrayDimCount</a></td>
<td class="dttd">Возвращает количество измерений массива значений типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayHighBound"title="lib en:System.Variants.VarArrayHighBound">VarArrayHighBound</a></td>
<td class="dttd">Возвращает верхнюю границу измерения в массиве значений типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayLock"title="lib en:System.Variants.VarArrayLock">VarArrayLock</a></td>
<td class="dttd">Блокирует массив значений типа variant и возвращает указатель на данные.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayLowBound"title="lib en:System.Variants.VarArrayLowBound">VarArrayLowBound</a></td>
<td class="dttd">Возвращает нижнюю границу измерения в массиве значений типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayOf"title="lib en:System.Variants.VarArrayOf">VarArrayOf</a></td>
<td class="dttd">Создает и заполняет одномерный массив вариантов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.VarArrayRedim"title="lib en:System.VarArrayRedim">VarArrayRedim</a></td>
<td class="dttd">Изменяет размер массива вариантов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayRef"title="lib en:System.Variants.VarArrayRef">VarArrayRef</a></td>
<td class="dttd">Возвращает ссылку на массив вариантов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarArrayUnlock"title="lib en:System.Variants.VarArrayUnlock">VarArrayUnlock</a></td>
<td class="dttd">Разблокирует массив вариантов.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarAsType"title="lib en:System.Variants.VarAsType">VarAsType</a></td>
<td class="dttd">Преобразует значение типа variant в заданный тип.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.VarCast"title="lib en:System.VarCast">VarCast</a></td>
<td class="dttd">Преобразует значение типа variant в заданный тип, сохраняя результат в переменной.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.VarClear"title="lib en:System.VarClear">VarClear</a></td>
<td class="dttd">Очищает значение типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.VarCopy"title="lib en:System.VarCopy">VarCopy</a></td>
<td class="dttd">Копирует значение типа variant.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarToStr"title="lib en:System.Variants.VarToStr">VarToStr</a></td>
<td class="dttd">Преобразует значение типа variant в строку.</td></tr>
<tr><td class="dttd"><a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.Variants.VarType"title="lib en:System.Variants.VarType">VarType</a></td>
<td class="dttd">Возвращает код типа для заданного значения типа variant.</td></tr>
</table></div></html><br />
svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-39257573946365359172012-05-19T21:02:00.000+04:002012-05-19T21:02:48.726+04:00Перегрузка операторов<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/en/Operator_Overloading" target="_blank">Operator Overloading</a> из справочной системы Delphi</cite><br/><br/>
<div align = "justify">
<h3>О перегрузке операторов</h3>
<p>Delphi для Win32 разрешает перегрузку некоторых функций или "операторов" внутри объявления записей. Имя функции оператора сопоставляется в программном коде с его символьным представлением. Например, оператор Add сопоставляется с символом + .
Компилятор генерирует вызов соответствующего перегружаемого оператора, сопоставляя контекст (то есть возвращаемый тип и тип параметров, использованных при вызове) с сигнатурой функции оператора.</p><a name='more'></a><br/>
<p>Следующая таблица показывает операторы языка Delphi, которые могут быть перегружены:</p>
<table class="dttbl" width="700">
<tr><th class="dtth">Оператор</th><th class="dtth">Категория</th><th class="dtth">Сигнатура объявления</th><th class="dtth">Совмещаемый символ</th></tr>
<tr><td class="dttd">Implicit</td><td class="dttd">Conversion</td><td class="dttd">Implicit(a : type) : resultType;</td><td class="dttd">implicit typecast</td></tr>
<tr><td class="dttd">Explicit</td><td class="dttd">Conversion</td><td class="dttd">Explicit(a: type) : resultType;</td><td class="dttd">explicit typecast</td></tr>
<tr><td class="dttd">Negative</td><td class="dttd">Unary</td><td class="dttd">Negative(a: type) : resultType;</td><td class="dttd">-</td></tr>
<tr><td class="dttd">Positive</td><td class="dttd">Unary</td><td class="dttd">Positive(a: type): resultType;</td><td class="dttd">+</td></tr>
<tr><td class="dttd">Inc</td><td class="dttd">Unary</td><td class="dttd">Inc(a: type) : resultType;</td><td class="dttd">Inc</td></tr>
<tr><td class="dttd">Dec</td><td class="dttd">Unary</td><td class="dttd">Dec(a: type): resultType</td><td class="dttd">Dec</td></tr>
<tr><td class="dttd">LogicalNot</td><td class="dttd">Unary</td><td class="dttd">LogicalNot(a: type): resultType;</td><td class="dttd">not</td></tr>
<tr><td class="dttd">Trunc</td><td class="dttd">Unary</td><td class="dttd">Trunc(a: type): resultType;</td><td class="dttd">Trunc</td></tr>
<tr><td class="dttd">Round</td><td class="dttd">Unary</td><td class="dttd">Round(a: type): resultType;</td><td class="dttd">Round</td></tr>
<tr><td class="dttd">In</td><td class="dttd">Set</td><td class="dttd">In(a: type; b: type) : Boolean;</td><td class="dttd">in</td></tr>
<tr><td class="dttd">Equal</td><td class="dttd">Comparison</td><td class="dttd">Equal(a: type; b: type) : Boolean;</td><td class="dttd">=</td></tr>
<tr><td class="dttd">NotEqual</td><td class="dttd">Comparison</td><td class="dttd">NotEqual(a: type; b: type): Boolean;</td><td class="dttd"><></td></tr>
<tr><td class="dttd">GreaterThan</td><td class="dttd">Comparison</td><td class="dttd">GreaterThan(a: type; b: type) Boolean;</td><td class="dttd">></td></tr>
<tr><td class="dttd">GreaterThanOrEqual</td><td class="dttd">Comparison</td><td class="dttd">GreaterThanOrEqual(a: type; b: type): Boolean;</td><td class="dttd">>=</td></tr>
<tr><td class="dttd">LessThan</td><td class="dttd">Comparison</td><td class="dttd">LessThan(a: type; b: type): Boolean;</td><td class="dttd"><</td></tr>
<tr><td class="dttd">LessThanOrEqual</td><td class="dttd">Comparison</td><td class="dttd">LessThanOrEqual(a: type; b: type): Boolean;</td><td class="dttd"><=</td></tr>
<tr><td class="dttd">Add </td><td class="dttd">Binary</td><td class="dttd">Add(a: type; b: type): resultType;</td><td class="dttd">+</td></tr>
<tr><td class="dttd">Subtract</td><td class="dttd">Binary</td><td class="dttd">Subtract(a: type; b: type) : resultType;</td><td class="dttd">-</td></tr>
<tr><td class="dttd">Multiply</td><td class="dttd">Binary</td><td class="dttd">Multiply(a: type; b: type) : resultType;</td><td class="dttd">*</td></tr>
<tr><td class="dttd">Divide</td><td class="dttd">Binary</td><td class="dttd">Divide(a: type; b: type) : resultType;</td><td class="dttd">/</td></tr>
<tr><td class="dttd">IntDivide</td><td class="dttd">Binary</td><td class="dttd">IntDivide(a: type; b: type): resultType;</td><td class="dttd">div</td></tr>
<tr><td class="dttd">Modulus</td><td class="dttd">Binary</td><td class="dttd">Modulus(a: type; b: type): resultType;</td><td class="dttd">mod</td></tr>
<tr><td class="dttd">LeftShift</td><td class="dttd">Binary</td><td class="dttd">LeftShift(a: type; b: type): resultType;</td><td class="dttd">shl</td></tr>
<tr><td class="dttd">RightShift</td><td class="dttd">Binary</td><td class="dttd">RightShift(a: type; b: type): resultType;</td><td class="dttd">shr</td></tr>
<tr><td class="dttd">LogicalAnd</td><td class="dttd">Binary</td><td class="dttd">LogicalAnd(a: type; b: type): resultType;</td><td class="dttd">and</td></tr>
<tr><td class="dttd">LogicalOr</td><td class="dttd">Binary</td><td class="dttd">LogicalOr(a: type; b: type): resultType;</td><td class="dttd">or</td></tr>
<tr><td class="dttd">LogicalXor</td><td class="dttd">Binary</td><td class="dttd">LogicalXor(a: type; b: type): resultType;</td><td class="dttd">xor</td></tr>
<tr><td class="dttd">BitwiseAnd</td><td class="dttd">Binary</td><td class="dttd">BitwiseAnd(a: type; b: type): resultType;</td><td class="dttd">and</td></tr>
<tr><td class="dttd">BitwiseOr</td><td class="dttd">Binary</td><td class="dttd">BitwiseOr(a: type; b: type): resultType;</td><td class="dttd">or</td></tr>
<tr><td class="dttd">BitwiseXor</td><td class="dttd">Binary</td><td class="dttd">BitwiseXor(a: type; b: type): resultType;</td><td class="dttd">xor</td></tr>
</table>
<p>Операторы, не перечисленные в этой таблице, не могут быть использованы для работы с классами или записями.
Методы перегруженных операторов не могут вызываться по имени в программном коде. Для доступа к методу оператора в определенном классе или записи см. <a href="http://docwiki.embarcadero.com/CodeExamples/en/OpOverloads_(Delphi)" target="blank">Code Example:OpOverloads_(Delphi)</a>. Идентификаторы операторов, включенные в список элементов класса или записи, начинаются с ключевого слова "operator" (пример:<a href="http://docwiki.embarcadero.com/Libraries/XE2/en/System.AnsiStringBase_Functions" target="_blank">System.AnsiStringBase_Functions</a>). Вы можете реализовать любой из вышеперечисленных операторов в собственных классах и записях.</p>
<p>Компилятор будет использовать для класса или записи оператор в том случае если:</p>
<ul>
<li>Для бинарных операторов: один из входных параметров должен быть классом;</li>
<li>Для унарных операторов классом должен быть либо входной параметр, либо возвращаемое значение;</li>
<li>Для логического оператора и побитового оператора, использующих один и тот же символ, логический оператор используется только когда операнды относятся к булевскому типу. Если тип класса этого оператора класса не является булевским, логический оператор будет использован только если другой операнд является булевским.</li>
</ul>
<p>Для распределительных или коммутативных свойств операции не делается предположений. Для бинарных операторов первым параметром является всегда левый операнд, а правый – вторым. Предполагается ассоциативность в порядке слева-направо при отсутствии явно указанных скобок.</p>
<p>Разрешение методов операторов выполняется через объединение доступных операторов для типов, использованных в операции (включая наследуемые операторы). Для операции, включающей два различных типа A и B: если тип A может быть неявно преобразован к В, а В может быть неявно преобразован к А, будет иметь место неоднозначная интерпретация. Неявные преобразования должны использоваться только там, где они абсолютно необходимы, а рефлексивность должна быть исключена. Наилучшим решением будет разрешить типу В неявно преобразовать себя в тип А, позволив типу А ничего не знать о типе В (или наоборот). </p>
<p>Основным правилом является то, что операторы не должны изменять операнды, а возвращать новое значение, созданное при работе с параметрами. </p>
<p>Перегружаемые операторы особенно часто используются в записях (то есть в значимых типах).</p>
<br>
<h3>Объявление перегрузки операторов</h3>
<p>Перегрузка операторов объявляется внутри классов или записей со следующим синтаксисом:</p>
<pre class="brush:pas">
type
typeName = record
class operator conversionOp(a: type): resultType;
class operator unaryOp(a: type): resultType;
class operator comparisonOp(a: type; b: type): Boolean;
class operator binaryOp(a: type; b: type): resultType;
end;
</pre>
<p>Реализация перегружаемых операторов также должна включать этот синтаксис:</p>
<pre class="brush:pas">
class operator typeName.conversionOp(a: type): resultType;
class operator typeName.unaryOp(a: type): resultType;
class operator typeName.comparisonOp(a: type; b: type): Boolean;
class operator typeName.binaryOp(a: type; b: type): resultType;</pre>
<p>Далее приведены некоторые примеры перегруженных операторов:</p>
<pre class="brush:pas">
type
TMyRecord = record
class operator Add(a, b: TMyRecord): TMyRecord;// Добавление двух операндов типа TMyRecord
class operator Subtract(a, b: TMyRecord): TMyRecord;// Вычитание типов TMyRecord
class operator Implicit(a: Integer): TMyRecord;// Неявное преобразование Integer к TMyRecord
class operator Implicit(a: TMyRecord): Integer;// Неявное преобразование TMyRecord к Integer
class operator Explicit(a: Double): TMyRecord;// Явное преобразование Double к TMyRecord
end;
// Пример реализации Add
class operator TMyRecord.Add(a, b: TMyRecord): TMyRecord;
begin
// ...
end;
var
x, y: TMyRecord;
begin
x := 12; // Неявное преобразование к Integer
y := x + x; // ВызовTMyRecord.Add(a, b: TMyRecord): TMyRecord
b := b + 100; // Вызов TMyRecord.Add(b, TMyRecord.Implicit(100))
end;
</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com3tag:blogger.com,1999:blog-1920033394599582564.post-58010791701963740412012-05-19T20:59:00.000+04:002013-03-12T15:12:42.079+04:00Вложенные типы<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/en/Nested_Type_Declarations" target="_blank">Nested Type Declarations</a> из справочной системы Delphi</cite><br/><br>
<div align = "justify">
<h3>Объявление вложенных типов</h3>
<p>Объявления типов могут быть вложены в объявление класса. Вложенные типы часто используются в объектно-ориентированном программировании. Они дают возможность не разделять концептуально связанные типы и избежать конфликтов имен. Синтаксис объявления вложенных типов может быть использован и при работе с компилятором Delphi для Win32.
</p><a name='more'></a><br/>
<h3>Синтаксис объявления вложенных типов</h3>
<p>nestedTypeDeclaration подчиняется обычным правилам синтаксиса объявления типов.</p>
<pre class="brush:pas">
type
className = class [abstract | sealed] (ancestorType)
memberList
type
nestedTypeDeclaration
memberList
end;</pre>
<p>Область объявления вложенного типа заканчивается первым упоминанием элемента, не являющегося идентификатором. Например, ключевыми словами procedure, class, type или спецификаторами области видимости.</p>
<p>К вложенным типам и к типам, которые их содержат, применяются обычные правила доступа. Вложенный тип может иметь доступ к переменной (полю, свойству или методу) класса-контейнера при указании ссылки на объект. Вложенный тип может иметь доступ к полям класса, свойствам класса и статичным методам класса без указания ссылки на объект, но при этом действуют обычные правила области видимости Delphi. </p>
<p>Вложенные типы не увеличивают размер класса-контейнера. Созадание экземпляр класса-контейнера не создает экземпляра вложенного типа. Вложенные типы связаны с классами-контейнерами только по контексту их объявления.</p>
<br>
<h3>Объявление и доступ к вложенным классам</h3>
<p>Следующий пример показывает, как объявить вложенный класс и как получить доступ к его полям и методам:</p>
<pre class="brush:pas">
type
TOuterClass = class
strict private
myField: Integer;
public
type
TInnerClass = class
public
myInnerField: Integer;
procedure innerProc;
end;
procedure outerProc;
end;</pre>
<p>Для реализации метода innerProc внутреннего класса, вам необходимо специфицировать его имя именем внешнего класса. То есть:</p>
<pre class="brush:pas">
procedure TOuterClass.TInnerClass.innerProc;
begin
...
end;</pre>
<p>Для доступа к элементам вложенного типа следует использовать точечную нотацию, как при доступе к элементам обычного класса. Например:</p>
<pre class="brush:pas">
var
x: TOuterClass;
y: TOuterClass.TInnerClass;
begin
x := TOuterClass.Create;
x.outerProc;
...
y := TOuterClass.TInnerClass.Create;
y.innerProc;
</pre><br>
<h3>Вложенные константы</h3>
<p>Константы могут быть объявлены внутри типов таким же образом, как и вложенные классы. Секции объявления констант завершаются такими же элементами, как и секции объявления вложенных типов, в частности, зарезервированными словами или спецификаторами видимости. Типизированные константы в этом случае запрещены, то есть вы не можете объявить вложенные константы значимых типов, таких как System.Currency или System.TDateTime.</p>
<p>Вложенные константы могут быть любого простого типа: порядковые, порядковые поддиапазоны, перечисления, строки и вещественные типы.</p>
<p>Следующий пример показывает объявление вложенных констант:</p>
<pre class="brush:pas">
type
TMyClass = class
const
x = 12;
y = TMyClass.x + 23;
procedure Hello;
private
const
s = 'A string constant';
end;
begin
Writeln(TMyClass.y); //Выводит значение y, 35.
end.</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com2tag:blogger.com,1999:blog-1920033394599582564.post-14871608779396692242012-05-19T20:55:00.000+04:002013-04-13T18:34:57.254+04:00Классы и записи - помощники<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/en/Class_and_Record_Helpers" target="_blank">Class and Record Helpers</a> из справочной системы Delphi</cite><br />
<br />
<div align = "justify"><h3>О классах и записях - помощниках</h3><p>Класс-помощник или запись-помощник – это типы, которые, будучи связанными с другими классами или записями вводят дополнительные названия методов и свойств, которые могут быть использованы в контексте связанного типа (или его потомков). Помощники являются дают возможность расширить класс без применения интерфейсов, что особенно полезно при работе с записями, для которых запрещено наследование. Помощник просто объявляет более широкую область видимости для компилятора при поиске идентификаторов. Когда вы объявляете класс-помощник или запись-помощник, вы указываете имя помощника и имя типа, который вы собираетесь им расширить. Вы можете использовать помощника в любом месте, где вы можете работать с расширенным классом или записью. Область видимости компилятора расширяется до области видимости исходного типа, плюс типа помощника.</p><a name='more'></a><br />
<p>Классы-помощники и записи-помощники дают возможность расширить тип, но они не могут рассматриваться как инструмент разработки нового кода. Работая с новым кодом вы должны всегда полагаться на обычное наследование и реализацию интерфейсов.</p><br />
<h3>Синтаксис помощника</h3><p>Синтаксис объявления класса-помощника: <pre class="brush:pas">type
identifierName = class|record helper [(ancestor list)] for TypeIdentifierName
memberList
end;</pre><p>ancestor list - опционален.</p><p>Помощник не может содержать объявления полей, но позволяет объявлять поля класса.</p><p>Правила области видимости и синтаксис memberList идентичны объявлению обычного класса или записи.</p><p>Вы можете объявить несколько помощников и связать их с одним типом, но видимым будет только один помощник, определенный в ближайшей области видимости. Область видимости класса-помощника или записи-помощника определяется обычным для Delphi образом (например, справа налево в секции uses модуля).</p><br />
<h3>Применение помощников</h3><p>Следующий пример показывает объявление класса-помощника (записи-помощники работают аналогичным образом):</p><div><pre class="brush:pas">type
TMyClass = class
procedure MyProc;
function MyFunc: Integer;
end;
...
procedure TMyClass.MyProc;
var X: Integer;
begin
X := MyFunc;
end;
function TMyClass.MyFunc: Integer;
begin
...
end;
...
type
TMyClassHelper = class helper for TMyClass
procedure HelloWorld;
function MyFunc: Integer;
end;
...
procedure TMyClassHelper.HelloWorld;
begin
Writeln(Self.ClassName); // Self ссылается на тип TMyClass, не на TMyClassHelper
end;
function TMyClassHelper.MyFunc: Integer;
begin
...
end;
...
var
X: TMyClass;
begin
X := TMyClass.Create;
X.MyProc; // Вызывает TMyClass.MyProc
X.HelloWorld; // Вызывает TMyClassHelper.HelloWorld
X.MyFunc; // Вызывает TMyClassHelper.MyFunc
</pre></div><p>Следует учесть, что вызывается функция MyFunc класса-помощника, поскольку тип класса-помощника имеет преимущество перед исходным типом класса. </p></div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com1tag:blogger.com,1999:blog-1920033394599582564.post-9134269921690602832012-05-19T20:49:00.001+04:002012-05-19T20:49:34.189+04:00Исключения (исключительные ситуации)<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/en/Exceptions" target="_blank">Exceptions</a> из справочной системы Delphi</cite><br/><br>
<div align = "justify">
<h3>Об исключениях</h3>
<p>Исключительная ситуация возникает при ошибке или прерывании нормального хода выполнения программы каким-либо событием. Исключение передает контроль выполнения программы обработчику исключительной ситуации, который позволяет отделить нормальную логику работы программы от обработки ошибок. Поскольку исключения являются объектами, они могут быть сгруппированы в иерархию, использующую наследование, а новые исключения могут объявляться без изменения уже готового кода. Исключение может передавать информацию (например, сообшение об ошибке) из точки возникновения исключительной ситуации к месту ее обработки.</p>
<p>Когда приложение подключает модуль SysUtils большая часть ошибок, возникающих при выполнении программы, автоматически преобразуется в исключения. Значительная часть ошибок, результатом которых могло бы стать завершение программы (такие как недостаток памяти, деление на ноль, ошибки общей защиты) перехватываются и обрабатываются.</p>
<a name='more'></a><br/>
<h3>Когда следует применять исключения</h3>
<p>Исключения обеспечивают удобный способ отслеживать ошибки при выполнении программы без применения массивных условных конструкций. Требования, накладываемые семантикой обработки исключительных ситуаций, влекут за собой увеличение объема кода и данных, а также снижение производительности приложения. По этому, не смотря на то, что существует возможность создавать исключения практически в любой ситуации, защищая любой блок исходного кода заключением его в структуру try...except или try...finally, на практике эти инструменты лучше применять только в особых случаях.</p>
<p>Обработка исключительных ситуаций подходит для:</p>
<ul>
<li>трудноопределимых ошибок, вероятность возникновения которых невелика, но последствия которых могут оказаться серьезными (например, аварийное завершение приложения); </li>
<li>ошибок, возникновение которых трудно отследить при помощи инструкций if...then ; </li>
<li>случаев, когда необходимо реагировать на исключительные ситуации, создаваемые операционной системой или другими программами, код которых вы не контролируете. </li>
</ul>
<p>Обычно исключения используются для обработки ошибок аппаратного обеспечения, памяти, обработки ввода/вывода и ошибок операционной системы.</p>
<p>Инструкции условных переходов зачастую являются лучшим способом обработки ошибок. Например, если перед тем как открыть файл, вы хотите убедиться, что файл существует, можно сделать это следующим образом:</p>
<pre class="brush:pas">
try
AssignFile(F, FileName);
Reset(F); // если файл не найден,
//возникает исключительная ситуация EInOutError
except
on Exception do ...
end;</pre>
<p>Но для экономии ресурсов можно использовать и другой способ:</p>
<pre class="brush:pas">
if FileExists(FileName) then // возвращает False если файл не найден
//исключительной ситуации не возникает
begin
AssignFile(F, FileName);
Reset(F);
end;</pre>
<p>Утверждения (Assertions) дают еще одну возможность проверки логических условий в вашем исходном коде. Если при выполнении инструкции Assert происходит ошибка, приложение либо завершается, либо (в том случае, если подключен модуль SysUtils) создает исключение SysUtils.EAssertionFailed. Утверждения должны использоваться только для проверки условий, невыполнения которых вы не ожидаете.</p>
<br>
<h3>Объявление типов исключений</h3>
<p>Исключения объявляются как и прочие классы. На самом деле в качестве исключения вы можете использовать экземпляр любого класса, но рекомендуется наследовать исключения от класса SysUtils.Exception, который объявлен в модуле SysUtils.</p>
<p>Вы можете объединять исключения в семейства при помощи наследования. Приведенные ниже объявления из модуля SysUtils объявляют семейство исключений для обработки математических ошибок:</p>
<pre class="brush:pas">
type
EMathError = class(Exception);
EInvalidOp = class(EMathError);
EZeroDivide = class(EMathError);
EOverflow = class(EMathError);
EUnderflow = class(EMathError);</pre>
<p>Сделав такие объявления, вы можете создать один обработчик исключений для SysUtils.EMathError, который сможет работать с SysUtils.EInvalidOp, SysUtils.EZeroDivide, SysUtils.Overflow и SysUtils.EUnderflow.</p>
<p>Классы исключений иногда содержат поля, методы или свойства для передачи дополнительной информации об ошибке:</p>
<pre class="brush:pas">
type EInOutError = class(Exception)
ErrorCode: Integer;
end;
</pre><br>
<h3>Инициирование и обработка исключений</h3>
<p>Для инициирования исключения вам необходимо использовать экземпляр класса исключения с инструкцией raise:</p>
<pre class="brush:pas">
raise EMathError.Create;</pre>
<p>Вообще говоря, инструкция инициирования исключения имеет следующий вид:</p>
<pre class="brush:pas">
raise object at address</pre>
<p>где object и at address являются опциональными. Когда указывается address, им может быть любое выражение, которое можно обработать как указатель. Обычно это указатель на процедуру или функцию. Например:</p>
<pre class="brush:pas">
raise Exception.Create('Missing parameter') at @MyFunction;</pre>
<p>Этой опцией можно инициировать исключение из участка стека, предшествующего тому, в котором на самом деле случилась ошибка. Когда исключение инициировано, то есть после вызова инструкции raise, управление им осуществляется по специальному принципу обработки исключительных ситуаций. Инструкция raise не возвращает управление в точку своего выполнения. Вместо этого управление передается обработчику исключений соответствующего класса, который находится в самом большом уровне вложенности блоков try except (т.е. относящийся к блоку, вход в который был осуществлен позже всего).</p>
<p>Например, приведенная ниже функция преобразует строку в целое число и инициирует исключение в том случае, если результирующее значение не находится в обозначенном диапазоне.</p>
<pre class="brush:pas">
function StrToIntRange(const S: string; Min, Max: Longint): Longint;
begin
Result := StrToInt(S); // StrToInt объявлен в SysUtils
if (Result < Min) or (Result > Max) then
raise ERangeError.CreateFmt('%d is not within the valid range of %d..%d',
[Result, Min, Max]);
end;</pre>
<p>Обратите внимание на метод CreateFmt , вызываемый в инструкции raise. Класс SysUtils.Exception и его потомки оснащены особыми конструкторами, которые предоставляют возможность создавать сообщения об ошибках и идентификаторы контекста.</p>
<p>Инициированное исключение автоматически разрушается после его обработки. Не следует пытаться разрушать исключения самостоятельно.</p>
<p><cite>Замечание: Вызов (инициирование) исключения в секции инициализации модуля может не привести к ожидаемому результату. Обычно обработка исключительных ситуаций выполняется из модуля SysUtils, который должен быть инициализирован прежде чем выполнять такие функции. Если исключительная ситуация имеет место в процессе инициализации – все инициализированные модули (включая SysUtils) финализируются, а исключение инициируется повторно. Затем, после перехвата, исключение обрабатывается (обычно завершением приложения). Инициирование исключений в секции финализации модуля также может не привести к нужному результату, в том случае, если на момент инициирования исключения модуль SysUtils уже финализирован.</cite></p>
<br>
<h4>Инструкции Try...except</h4>
<p>Исключительные ситуации обрабатываются обрабатываются внутри конструкций try...except. Например:</p>
<pre class="brush:pas">
try
X := Y/Z;
except
on EZeroDivide do HandleZeroDivide;
end;</pre>
<p>Эта конструкция предпринимает попытку деления y на z, а в случае возникновения исключительной ситуации SysUtils.EZeroDivide вызывает подпрограмму HandleZeroDivide.</p>
<p>Синтаксис инструкции try...except:</p>
<pre class="brush:pas">
try statements except exceptionBlock end</pre>
<p>где statements – это последовательность инструкций, разделенных точками с запятой (;), а exceptionBlock – может быть: </p>
<ul>
<li>еще одной последовательностью инструкций</li>
<li>еще одной последовательностью инструкций или последовательностью обработчиков исключений, разделяемых инструкциями else.</li>
</ul>
<p>Обработчик исключения имеет вид:</p>
<pre class="brush:pas">
on identifier: type do statement</pre>
<p>где identifier: необязателен (если присутствует – может быть любым допустимым идентификатором), type – это тип для представления исключений, а statement – это любая инструкция.</p>
<p>Инструкция try...except выполняет команды из изначального списка. Если исключительных ситуаций не возникло, блок исключений игнорируется и управление передается следующей части программы.</p>
<p>Если в процессе выполнения списка команд возникла исключительная ситуация (она может быть инициирована инструкцией raise в списке команд или внутри процедуры или функции, вызываемой из этого списка) предпринимается попытка обработки исключительной ситуации:</p>
<p>Если какой-либо из обработчиков в exception block подходит для обработки инициированного исключения, управление передается первому такому обработчику. Обработчик подходит для исключения только в том случае, когда тип, указанный в нем, является типом исключения или предком его класса.</p>
<p>Если обработчика не найдено, управление передается инструкции в секции else (если таковая присутствует).</p>
<p>Если блок обработчиков – это просто последовательность инструкций без каких либо обработчиков – управление передается первой инструкции в этой последовательности.</p>
<p>Если ни одно из указанных выше условий не выполнено, поиск продолжается в блоке обработчиков внешней инструкции try...except , выход из которой еще не выполнен. Если и там не находится соответствующего обработчика, секции else или последовательности инструкций, поиск продолжается в следующей внешней инструкции и так далее. Если в самой внешней инструкции исключительная ситуация не будет обработана – приложение завершается.</p>
<p>Когда исключительная ситуация обработана, в стеке находится процедура или функция, содержащая инструкцию try...except , в которой произошла обработка и управление передается выполненному обработчику, секции else или последовательности инструкций. Этот процесс отменяет все вызовы процедур и функций, имевшие место после входа в блок try...except, в котором исключительная ситуация была обработана. Объект исключения автоматически разрушается вызовом деструктора Destroy и упрвление передается инструкции, следующей за блоком try...except. (Если вызов стандартных процедур Exit, Break или Continue выводит управление из обработчика, объект исключения также разрушается).</p>
<p>В следующем примере первый обработчик обрабатывает исключение деления на ноль, второй – ошибки переполнения, а последний – остальные математические исключения. SysUtils.EMathError указан последним в блоке обработчиков, так как он является предком обоих этих классов. Если бы он указан первым, последующие обработчики никогда не были бы вызваны:</p>
<pre class="brush:pas">
try
...
except
on EZeroDivide do HandleZeroDivide;
on EOverflow do HandleOverflow;
on EMathError do HandleMathError;
end;</pre>
<p>В обработчике исключения перед именем класса исключения можно указать идентификатор. Таким образом объявляется идентификатор для представления объекта исключения в процессе выполнения инструкции, следующей за on...do. Видимость идентификатора ограничивается этой инструкцией. Например: </p>
<pre class="brush:pas">
try
...
except
on E: Exception do ErrorDialog(E.Message, E.HelpContext);
end;</pre>
<p>Если блок исключения имеет секцию else, в этой секции происходит обработка всех исключений, которые не были обработаны в блоке обработчика исключения. То есть:</p>
<pre class="brush:pas">
try
...
except
on EZeroDivide do HandleZeroDivide;
on EOverflow do HandleOverflow;
on EMathError do HandleMathError;
else
HandleAllOthers;
end;</pre>
<p>В этом примере секция else обрабатывает все исключения, не являющимися SysUtils.EMathError.</p>
<p>Блок исключения, не содержащий обработчиков, но содержащий список инструкций, обрабатывает любые исключения. Например:</p>
<pre class="brush:pas">
try
...
except
HandleException;
end;</pre>
<p>Здесь подпрограмма HandleException обрабатывает все исключенительные ситуации, которые возникают при выполнении инструкций между try и except.</p>
<br>
<h4>Повторное инициирование исключений</h4>
<p>Когда ключевое слово raise включается в блок исключения без указания ссылки на объект, оно инициирует исключение, уже обработанное в блоке. Это позволяет обработчику исключения среагировать на ошибку и затем повторно инициировать исключение. Повторное инициирование исключения может оказаться полезным, когда процедура или функция должна освободить память после своего выполнения, но не может полностью обработать исключение. </p>
<p>Например, функция GetFileList создает объект TStringList и заполняет его именами файлов, расположенными в определенной для поиска папке: </p>
<pre class="brush:pas">
function GetFileList(const Path: string): TStringList;
var
I: Integer;
SearchRec: TSearchRec;
begin
Result := TStringList.Create;
try
I := FindFirst(Path, 0, SearchRec);
while I = 0 do
begin
Result.Add(SearchRec.Name);
I := FindNext(SearchRec);
end;
except
Result.Free;
raise;
end;
end;</pre>
<p>GetFileList создает объект TStringList, затем применяет функции FindFirst и FindNext (определены в модуле SysUtils) для его инициализации. Если в процессе инициализации происходит ошибка – например, из-за того, что путь задан неверно или для заполенения списка строк недостаточно памяти - GetFileList нужно высвободить память созданного списка при том, что вызывающая подпрограмма ничего не знает о его существовании. По этой причине, инициализация списка строк выполняется внутри инструкции try...except. Если возникает исключительная ситуация, блок обработки исключения разрушает список строк и повторно инициирует исключение.</p>
<br>
<h4>Встроенные исключения</h4>
<p>Код, выполняемый в обработчике исключения может сам инициировать и обрабатывать исключительные ситуации. Поскольку исключения обрабатываются внутри обработчика, они не оказывают влияния на первичное исключение. Тем не менее, если исключение инициированное внутри обработчика, не обрабатывается в нем, первичное исключение теряется. Эту ситуацию иллюстрирует функция Tan, код которой приведен ниже: </p>
<pre class="brush:pas">
type
ETrigError = class(EMathError);
function Tan(X: Extended): Extended;
begin
try
Result := Sin(X) / Cos(X);
except
on EMathError do
raise ETrigError.Create('Invalid argument to Tan');
end;
end;</pre>
<p>Если исключение SysUtils.EMathError инициируется при выполнении Tan, обработчик инициирует исключение ETrigError. Поскольку Tan не имеет обработчика для ETrigError, это исключение выходит из обработчика, тем самым вызывая разрушение исключения SysUtils.EMathError. Вызывающей подпрограмме передается ETrigErrorexception, инициированное внутри функции Tan.</p>
<br>
<h4>Инструкции Try...finally</h4>
<p>Иногда вам необходимо быть уверенными, что некоторые части операции выполнены, вне зависимости от того, прерывается ли выполнение операции исключением или нет. Например, если подпрограмма получает управление ресурсом, важно чтобы память, которую он занимает, была освобождена несмотря на то, что подпрограмма завершается ошибкой. В таких ситуациях вы можете использовать инструкцию try...finally.</p>
<p>В следующем примере показан код, который открывает и обрабатывает файл, при этом файл будет в конечном итоге закрыт, даже если в процессе обработки произойдет ошибка:</p>
<pre class="brush:pas">
Reset(F);
try
... // process file F
finally
CloseFile(F);
end;
</pre>
<p>Синтаксис инструкции try...finally выглядит следующим образом:</p>
<pre class="brush:pas">
try statementList1 finally statementList2 end</pre>
<p>где каждый statementList – это последовательность инструкций, разделенных точками с запятой (;). Инструкция try...finally выполняет инструкции в statementList1 (секция try). Если выполнение statementList1 завершается без ошибок, выполняется statementList2 (секция finally). Если в процессе выполнения statementList1 возникает исключительная ситуация, управление передается в statementList2, когда statementList2 завершает свою работу исключение инициируется повторно. Если в процессе выполнения statementList1 происходит вызов процедур Exit, Break или Continue – управление выходит из statementList1 , statementList2 выполняется автоматически. Таким образом секция finally выполняется всегда, вне зависимости от того, как завершается работа секции try.</p>
<p>Если в секции finally возникает исключительная ситуация, которая не обрабатывается, исключение выводится из из инструкции try...finally, а исключение, инициированное в секции try, разрушается. Таким образом, секция finally должна обработать все внутренние исключительные ситуации с тем, чтобы они не были переданы за пределы секции.</p>
<br>
<h3>Стандартные классы подпрограммы для работы с исключениями</h3>
<p>Модули SysUtils и System объявляют несколько стандартных подпрограмм для обработки исключений, включая ExceptObject, ExceptAddr и ShowException. SysUtils, System и прочие модули включают в себя множество классов исключений, которые наследуются от SysUtils.Exception (или от OutlineError).</p>
<p>Класс SysUtils.Exception имеет свойства Message и HelpContext, которые можно использовать для передачи описания ошибки и идентификатора контекста для контекстной онлайн документации. Кроме того, этот класс объявляет конструкторы, позволяющие различными способами задавать описание и идентификатор контекста. </p>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-20689008861087266952012-05-19T20:38:00.000+04:002012-05-19T20:38:46.834+04:00Ссылки на класс<cite>Перевод раздела <a href="http://docwiki.embarcadero.com/RADStudio/en/Class_References" target="_blank">Class References</a> из справочной системы Delphi</cite><br/><br/>
<div align = "justify">
<h3>Тип ссылки на класс</h3>
<p>Тип ссылки на класс иногда называют метаклассом, а его объявление имеет вид:</p>
<pre class="brush:pas">
class of type</pre>
<p>где type – это любой класс. Идентификатор type обозначает значение, тип которого – это тип класса. Если type1 является предком type2, тогда тип class of type2 совместим по присваиванию с типом class of type1. То есть:</p><a name='more'></a><br/>
<pre class="brush:pas">
type TClass = class of TObject;
var AnyObj: TClass;</pre>
<p>объявляет переменную AnyObj, которая может содержать ссылку на любой класс. (Объявление типа ссылки на класс не может производиться непосредственно при объявлении переменной или в списке параметров.) Вы можете присвоить переменной типа ссылка на класс значение nil.</p>
<p>Чтобы понять как можно использовать этот тип, рассмотрим объявление конструктора Classes.TCollection (в модуле Classes):</p>
<pre class="brush:pas">
type TCollectionItemClass = class of TCollectionItem;
...
constructor Create(ItemClass: TCollectionItemClass);</pre>
<p>Это объявление говорит, что для создание экземпляра Classes.TCollection, вам необходимо передать конструктору имя класса, наследуемого от Classes.TCollectionItem.</p>
<p>Ссылки на класс полезны, когда вам необходимо выполнить метод класса или виртуальный конструктор для класса или объекта, тип которого неизвестен на этапе компиляции.</p>
<br>
<h4>Конструкторы и ссылки на класс</h4>
<p>Конструктор может быть вызван при помощи переременной, содержащей ссылку на класс. Это делает возможным создание объектов, тип которых неизвестен на этапе компиляции. Например:</p>
<pre class="brush:pas">
type TControlClass = class of TControl;
function CreateControl(ControlClass: TControlClass;
const ControlName: string; X, Y, W, H: Integer): TControl;
begin
Result := ControlClass.Create(MainForm);
with Result do
begin
Parent := MainForm;
Name := ControlName;
SetBounds(X, Y, W, H);
Visible := True;
end;
end;</pre>
<p>Функция CreateControl требует параметр для определения типа элемента управления, который необходимо создать. Она использует этот параметр, чтобы вызвать конструктор класса. Поскольку идентификаторы классов представляют собой значения типа ссылки на класс, при вызове CreateControl можно указать идентификатор класса чтобы создать его экземпляр. Например:</p>
<pre class="brush:pas"> CreateControl(TEdit, 'Edit1', 10, 10, 100, 20);</pre>
<p>Конструкторы, вызываемые при помощи ссылок на класс, обычно являются виртуальными. Реализация конструктора, подключаемая при вызове, зависит от типа, который будет передан в процессе выполнения программы. </p>
<br>
<h3>Операторы классов</h3>
<p>Методы классов работают со ссылками на класс. Каждый класс наследует от TObject два метода: ClassType и ClassParent. Эти методы возвращают соответственно ссылки на класс объекта и класс его непосредственного предка. Оба метода возвращают значение типа TClass (где TClass = class of TObject), которое может быть преобразовано к типу определенного класса. Каждый класс наследует метод InheritsFrom, который выполняет проверку наследования от определенного класса. Эти методы используются операторами is и as,.что позволяет не вызывать их в обычной ситуации.</p>
<br>
<h4>Оператор is</h4>
<p>Проверяющий оператор is, применяется для определения класса объекта в режиме выполнения программы. Выражение </p>
<pre class="brush:pas"> object is class</pre>
<p>возвращает True если object – это экземпляр типа class или любого из его предков. В противном случае он возвращает False. (Если object имеет значение nil, оператор вернет False.) Если объявленный тип объекта не имеет отношения к классу, то есть классы отличаются, и проверяемый класс не является предком класса экземпляра, происходит ошибка компиляции. Например:</p>
<pre class="brush:pas"> if ActiveControl is TEdit then TEdit(ActiveControl).SelectAll;</pre>
<p>Эта инструкция после проверки принадлежности объекта к классу TEdit (или какому-либо его потомку) преобразует переменную к типу TEdit.</p>
<br>
<h4>Оператор as</h4>
<p>Оператор as выполняет безопасное преобразование типов. Выражение вида</p>
<pre class="brush:pas"> object as class</pre>
<p>возвращает ссылку на объект object, который имеет тип, преобразованный к class. При выполнении программы объект должен быть экземпляром класса class, его потомка, или иметь значение nil. В противном случае возникает исключительная ситуация. Если при компиляции объявленный тип объекта object не имеет отношения к class – то есть ни один из типов не является предком другого типа – возникает ошибка компиляции. Например:</p>
<pre class="brush:pas">
with Sender as TButton do
begin
Caption := '&Ok';
OnClick := OkClick;
end;</pre>
<p>Правила приоритета операторов зачастую требуют, чтобы преобразование типов при помощи оператора as заключалось в скобки:</p><pre class="brush:pas">
(Sender as TButton).Caption := '&Ok';
</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-76283969091891851062012-05-19T20:36:00.000+04:002012-05-19T21:14:43.038+04:00Трудности перевода (Часть 2)<div align="justify">
<p>Вообще перевод технических документов зачастую становится делом неблагодарным, поскольку в процессе можно упустить нюансы, а местами вообще исказить смысл. Люди опытные именно по этой причине читают документацию в оригинале. К сожалению, опытными мы становимся только постепенно, а читать документацию о незнакомом продукте на неродном языке все-таки надо, хоть это и не всегда удобно (хотя, наверняка стоит тренировать себя).</p></p><a name='more'></a><br/>
<p>Теперь о нюансах и искажении смысла. За собой такого не замечаю, но если кто укажет мне на прецеденты – буду благодарен. Обращаю внимание, что чем дальше продвигаюсь в переводах, тем сложнее они даются. Современная терминология, связанная с языками программирования очень быстро развивается и, похоже, что люди, связанные с этим процессом напрямую, не слишком утруждаются, вводя новые термины и лексику, с ними связанную. Ругаться на эту тему не планирую, но местами оно утомляет.</p>
<p>Анонимные методы, исключения, метаклассы – отличный способ поломать себе голову на предмет: "как это вообще может звучать по-русски?". Однако долго рассуждать "вообще", наверное, не стоит. Перейдем к конкретике.</p>
<p>Если говорить об исключениях, то слово "exception" обозначает вроде бы сразу два понятия. С одной стороны – это исключительная ситуация, ошибка в приложении, которая может быть связана с отказом аппаратного или программного обеспечения. С другой стороны, этим же словом обозначают некую конструкцию в программе, класс, включающий в себя свойства и методы, который должен помочь обработать эту ошибку. Мне кажется, что наличие уже такой двусмысленности может удручать человека, читающего документацию.</p>
<p>Далее лексика, связанная с этим термином: catch exception, raise (re-raise) exception, handle exception. Предлагаю разобраться с нею повнимательнее.</p>
<p>catch exception – обычно переводят как "перехватывать исключение". При этом чаще всего речь идет о том, что приложение обнаруживает некий сбой аппаратного или программного обеспечения (читай: исключительную ситуацию).</p>
<p>raise exception – наверное, самое противоречивое словосочетание. В предыдущих случаях наше приложение реагировало на внешние факторы, а здесь уже идет речь об обработке ошибок, возникающих при выполнении некоего кода внутри системы. Исключительная ситуация возникает в процессе выполнения кода, который контролирует программист. Причем и степень "ошибочности" определяет разработчик. То есть понятно, что ваше приложение не может разделить на ноль или прочитать несуществующий файл, но вы в вашем приложении можете создать исключение (класс), которое будет реагировать на более безобидные вещи. Хоть на неверную длину строки, хранящей имя пользователя или еще что-нибудь в том же духе. В этом случае, код, который вы разработали, будет создавать класс исключения и вводить его в работу. И этот процесс обзывается raising the exception. </p>
<p>Наверняка вы сталкивались с таким вариантом перевода этого словосочетания, как "возбуждать исключение". Он такой… довольно распространенный. Мне кажется, что даже человека, регулярно общающегося с женщинами, должно коробить от такого;-)) Как тогда переводить "re-raise the exception"? Перевозбуждать исключение? Мне кажется, это уже ни в какие ворота;-) </p>
<p>Впрочем, я читал уже где-то обсуждения на тему этой фразы. Не помню точно, к чему там пришли в итоге, но вроде бы как ни к чему толковому. Моя версия такая: раз возникновение ошибки инициирует разработчик, значит и к термину исключение следует применять этот вариант. То есть исключение может быть инициировано. Или вызвано. Но вариант с инициированием мне больше нравится. При этом, если речь идет о первом значении слова exception (ошибка, обнаруженная приложением в процессе работы), "raising the exception" все-таки есть смысл переводить как "возникновение исключительной ситуации". В общем, не все тут просто и однозначно. Путаница возможна.</p>
<p>Такая же неоднозначность, как мне кажется, присутствует, когда читаешь "handle the exception". В одной ситуации приложение обрабатывает системную ошибку (исключительную ситуацию), в другой – работает с классом исключения.</p>
<p>Exception – неоднозначный термин. Был бы он один такой, было бы проще. Увы, это не совсем так. Например, когда читаешь документацию в разделе Classes and objects, постоянно сталкиваешься со словосочетанием "class member", то есть, если переводить дословно, то это "член класса". Опять же… мы тут, в России, точно знаем, что члены, в основном, могут быть в партии;-) В классе же могут быть ученики, ну или, на худой конец, представители. И я лично очень благодарен человеку, писавшему документацию по Delphi, который включил туда фразу:</p>
<p><cite>The fields, methods, and properties of a class are called its components or members.</cite></p>
<p>То есть "Поля, методы и свойства класса называют его <i>компонентами</i> или членами". В противном случае, была бы еще одна головная боль – подбирать эвфемизмы для переводов.</p>
<p>О чем еще хочется упомянуть в этом контексте? На самом деле, о многом;-) Когда в процессе перевода сталкиваешься с понятием "class reference"… В девяти из десяти случаев предложение, в котором упоминаются такие вещи, настолько мудреное, что переводить можно хоть как "ссылка на класс", хоть как "метакласс" – для восприятия легче не будет. Чаще всего в результате перевода не может получиться предложения, которое сможет воспринять человек без специальной подготовки. То есть без стакана не разберешься. Так эта подготовка называется.</p>
<p>Комментарии по-прежнему приветствуются. Следующую часть "Трудностей перевода" следует ожидать видимо по мере приближения к дженерикам. Та еще песня будет видимо. За сим откланиваюсь. До новых встреч в эфире.</p>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com1tag:blogger.com,1999:blog-1920033394599582564.post-66584233239909912682012-05-03T03:24:00.000+04:002012-05-03T03:24:08.537+04:00События<cite>Перевод из справочной системы Delphi. Оригинал: <a href="http://docwiki.embarcadero.com/RADStudio/en/Events" target="_blank">Events</a></cite>
<div align = "justify">
<br>
<h3>О Событиях</h3>
<p>Событие связывает некое проишествие (случай) в системе с программным кодом, который реагирует на это проишествие. Проишествие запускает выполнение процедуры или функции, называемыми обработчиками события. Обработчик события выполняет действия, необходимые для реакции на это проишествие. События позволяют настроить поведение компонентов при разработке программы или в режиме ее выполнения. Для изменения поведения компонента, следует заменить обработчик события на собственный, который будет иметь желаемое поведение. </p>
<a name='more'></a><br>
<h3>События-свойства и обработчики событий</h3>
<p>Компоненты, которые разработаны в Delphi, используют свойства для указания обработчика события, который должен быть выполнен в определенном случае. По договоренности имя события-свойства начинается с "On", а свойство реализуется полем, а не методами чтения/записи. Значение, хранящееся в свойстве – это указатель на метод, ссылающийся на процедуру-обработчик события.</p>
<p>В следующем примере класс TObservedObject включает в себя объявление события OnPing, которое имеет тип TPingEvent. Поле FOnPing используется для хранения обработчика события. Обработчик события в этом примере - TListener.Ping, выводит надпись 'TListener has been pinged!'.</p>
<pre class="brush:pas">
program EventDemo;
{$APPTYPE CONSOLE}
type
{ Объявление процедурного типа }
TPingEvent = procedure of object;
{ "Изучаемый" объект }
TObservedObject = class
private
FPing: TPingEvent;
public
property OnPing: TPingEvent read FPing write FPing;
{ Запуск события, если что-либо зарегистрировано }
procedure TriggerEvent();
end;
{ Класс-слушатель }
TListener = class
procedure Ping;
end;
procedure TObservedObject.TriggerEvent;
begin
{ вызов зарегистрированного события только если существует слушатель }
if Assigned(FPing) then
FPing();
end;
procedure TListener.Ping;
begin
Writeln('TListener has been pinged.');
end;
var
ObservedObject: TObservedObject;
Listener: TListener;
begin
{ Создание экземпляров объектов }
ObservedObject := TObservedObject.Create();
Listener := TListener.Create();
{ Регистрация обработчика события}
ObservedObject.OnPing := Listener.Ping;
{ Запуск события }
ObservedObject.TriggerEvent();//выведет 'TListener has been pinged'
Readln; // пауза консоли перед завершением
end.
</pre><br><br>
<h3>Запуск нескольких обработчиков событий</h3>
<p>В Delphi для Win32 с событием может быть связан только один обработчик. Если несколько обработчиков должны быть запущены для реакции на событие, связанный с событием обработчик должен выполнить вызов остальных обработчиков. В следующем примере класс, порожденный от TListener, с именем TListenerSubclass имеет собственный обработчик события с именем Ping2. Обработчик события Ping2 должен явно вызвать обработчик TListener.Ping для того, чтобы он среагировал на событие OnPing:</p>
<pre class="brush:pas">
program EventDemo2;
{$APPTYPE CONSOLE}
type
{ Объявление процедурного типа }
TPingEvent = procedure of object;
{ "Изучаемый" объект }
TObservedObject = class
private
FPing: TPingEvent;
public
property OnPing: TPingEvent read FPing write FPing;
{ запуск события, если что-либо зарегистрировано }
procedure TriggerEvent();
end;
{ Класс-слушатель }
TListener = class
procedure Ping;
end;
{ Потомок класса-слушателя }
TListenerSubclass = class(TListener)
procedure Ping2;
end;
procedure TObservedObject.TriggerEvent;
begin
{ вызов зарегистрированного события только если существует слушатель }
if Assigned(FPing) then
FPing();
end;
procedure TListener.Ping;
begin
Writeln('TListener has been pinged.');
end;
procedure TListenerSubclass.Ping2;
begin
{ Вызов метода базового класса }
Self.Ping();
Writeln('TListenerSubclass has been pinged.');
end;
var
ObservedObject: TObservedObject;
Listener: TListenerSubclass;
begin
{ Создание экземпляров объектов }
ObservedObject := TObservedObject.Create();
Listener := TListenerSubclass.Create();
{ Регистрация обработчика события }
ObservedObject.OnPing := Listener.Ping2;
{ Запуск события }
ObservedObject.TriggerEvent();//выведет 'TListener has been pinged'
//затем 'TListenerSubclass has been pinged'
Readln; // пауза консоли перед завершением
</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com1tag:blogger.com,1999:blog-1920033394599582564.post-37874660208132603282012-05-03T03:05:00.000+04:002012-05-03T03:19:27.017+04:00Свойства<cite>Перевод из справочной системы Delphi. Оригинал: <a href="http://docwiki.embarcadero.com/RADStudio/en/Properties" target="_blank">Properties</a></cite>
<div align = "justify"><br>
<h3>О свойствах</h3>
<p>Свойство, как и поле, определяет атрибут объекта. Однако, в то время как поле попросту является местом хранения, содержимое которого может быть проверено и отредактировано, свойство связывает с процессами чтения и записи данных специальные действия. Свойства обеспечивают контроль доступа к атрибутам объекта и позволяют им быть вычисляемыми. </p>
<a name='more'></a>
<p>В объявление свойств включается имя и тип, спецификатор доступа. Синтаксис объявления свойств:</p>
<pre class="brush:pas">
property propertyName[indexes]: type index integerConstant specifiers;
</pre>
где <br>
<p>propertyName – это любой допустимый идентификатор.</p>
<p>[indexes] – опционален и является последовательностью объявлений параметров, разделяемых точками с запятой (;). Каждое объявление параметра имеет вид: identifier1, ..., identifiern: type.</p>
<p>Тип – должен быть объявленным заранее идентификатором типа. То есть, объявление свойства вида Num: 0..9 ... не является допустимым.</p>
<p>Раздел index integerConstant является опциональным.</p>
<p>specifiers – это последовательность спецификаторов read, write, stored, default (или nodefault), а также спецификаторов реализации. Каждое объявление свойство должно содержать по крайней мере один из спецификаторов read или write.</p>
<p>Свойства определяются своими спецификаторами доступа. В отличии от полей, свойства не могут передаваться как параметры по ссылке и не могут быть операндом для оператора @. Причина этого заключается в том, что свойство физически может отсутствовать в памяти. Например, оно может иметь метод чтения, который получает значение из базы данных или генерирует случайное значение.</p>
<br/>
<h3>Доступ к свойствам</h3>
<p>Каждое свойство имеет либо спецификатор read, либо спецификатор write, либо оба этих спецификатора. Эти спецификаторы называются спецификаторами доступа и имеют следующий вид:</p>
<pre class="brush:pas">
read fieldOrMethod
write fieldOrMethod
</pre>
<p>где fieldOrMethod - это имя поля или метода, объявленного в этом же классе или в классе-предке.</p>
<p>Если объявление fieldOrMethod присутствует в этом же классе, оно должен предшествовать объявлению свойства. Если оно присутствует в классе-предке, оно должно быть видно из класса-наследника. То есть это не может быть объявлением поля или метода с видимостью private в классе-предке, объявленном в другом модуле. </p>
<p>Если fieldOrMethod является полем, оно должно быть одного типа со свойством.</p>
<p>Если fieldOrMethod – это метод, он не может быть динамическим, а если он является виртуальным, он не может быть перегружен. Кроме того, методы доступа к свойствам с видимостью published должны быть объявлены с конвенцией вызова по умолчанию.</p>
<p>При использовании в спецификаторах чтения, если fieldOrMethod – это метод, он должен быть функцией без параметров, возвращающей тип, идентичный типу свойства (Исключением может быть только метод чтения для индексируемого свойства или свойства-массива).</p>
<p>При использовании в спецификаторах записи, если fieldOrMethod является методом, он должен принимать один параметр (возможно больше, если это свойство-массив или индексированное свойство), передаваемый по ссылке или по значению, и совпадающий по типу с типом свойства. </p>
<p>Например, при объявлении:</p>
<pre class="brush:pas">
property Color: TColor read GetColor write SetColor;
</pre>
<p>Объявление метода GetColor должно иметь вид:</p>
<pre class="brush:pas">
function GetColor: TColor;
</pre>
<p>а метод SetColor может быть объявлен одним из нижеприведенных способов:</p>
<pre class="brush:pas">
procedure SetColor(Value: TColor);
procedure SetColor(const Value: TColor);
//Название параметра, передаваемого SetColor', естественно необязательно должно быть именно таким
</pre>
<p>Когда к свойству обращаются в выражении, его значение читается из поля или определяется при помощи метода, указанного в спецификаторе чтения. Когда свойство включается в инструкцию присваивания, его значение записывается в поле или при помощи метода, указанного в спецификаторе записи.</p>
<p>В следующем примере объявляется класс с именем TCompass, который имеет свойство Heading с видимостью published. Значение свойства Heading читается из поля FHeading и записывается при помощи процедуры SetHeading:</p>
<pre class="brush:pas">
type
THeading = 0..359;
TCompass = class(TControl)
private
FHeading: THeading;
procedure SetHeading(Value: THeading);
published
property Heading: THeading read FHeading write SetHeading;
...
end;
</pre>
<p>В данном объявлении инструкции:</p>
<pre class="brush:pas">
if Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;
</pre>
<p>соответствуют:</p>
<pre class="brush:pas">
if Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);
</pre>
<p>В объявлении класса TCompass с чтением свойства Heading не связывается никакого метода, операция чтения значения этого свойства сводится к извлечению значения из поля FHeading. С другой стороны, присваивание значения свойству Heading интерпретируется как вызов метода SetHeading, который по всей видимости, записывает новое значение в поле FHeading и выполняет какие-то дополнительные инструкции. Реализация метода SetHeading может выглядеть следующим образом:</p>
<pre class="brush:pas">
procedure TCompass.SetHeading(Value: THeading);
begin
if FHeading <> Value then
begin
FHeading := Value;
Repaint; // перерисовать интерфейс для отображения нового значения
end;
end;
</pre>
<p>Свойство, объявление которого включает только спецификатор чтения, является свойством только для чтения (read-only property), прочие свойства, объявления которых содержат только спецификатор записи являются свойствами только для записи (write-only property). Не допускается присваивание значения свойству только для чтения или использование свойства только для записи в выражениях.</p>
<br/>
<h3>Свойства-массивы</h3>
<p>Свойства-массивы – это индексированные свойства. Они могут представлять, например, элементы списка, дочерние элементы управления, относящиеся к элементу управления-контейнеру, пиксели в битмапе.</p>
<p>Объявления свойств-массивов включает в себя список параметров, который определяет имена и типы индексов. Например:</p>
<pre class="brush:pas">
property Objects[Index: Integer]: TObject read GetObject write SetObject;
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
property Values[const Name: string]: string read GetValue write SetValue;
</pre>
<p>Формат списка параметров индекса аналогичен списку параметров процедуры или функции, за исключением того, что объявления параметров заключаются в квадратные скобки вместо круглых. В отличии от массивов, которые могут иметь параметры только порядкового типа, для свойств-массивов разрешены параметры любого типа. </p>
<p>Для свойств-массивов в спецификаторах доступа должны быть указаны методы, а не поля. Метод в спецификаторе чтения должен быть функцией, принимающей то же количество параметров, что и список параметров индекса (параметры также должны совпадать и по типу и порядку следования) и возвращающей значение типа, соответствующего типу свойства. Метод в спецификаторе записи должен быть процедурой, которая принимает такое же количество параметров, что и список параметров индекса (параметры также должны соответствовать по типу и порядку следования) плюс дополнительный параметр-ссылка или значение, совпадающий по типу с типом свойства.</p>
<p>Методы доступа для свойств-массивов из предыдущего примера могут быть объявлены следующим образом:</p>
<pre class="brush:pas">
function GetObject(Index: Integer): TObject;
function GetPixel(X, Y: Integer): TColor;
function GetValue(const Name: string): string;
procedure SetObject(Index: Integer; Value: TObject);
procedure SetPixel(X, Y: Integer; Value: TColor);
procedure SetValue(const Name, Value: string);
</pre>
<p>Доступ к свойствам-массивам выполняется по индексированному идентификатору: </p>
<pre class="brush:pas">
if Collection.Objects[0] = nil then Exit;
Canvas.Pixels[10, 20] := clRed;
Params.Values['PATH'] := 'C:\BIN';
</pre>
<p>Что соответствует:</p>
<pre class="brush:pas">
if Collection.GetObject(0) = nil then Exit;
Canvas.SetPixel(10, 20, clRed);
Params.SetValue('PATH', 'C:\BIN');
</pre>
<p>За объявлением свойства-массива может следовать директива default, которая обозначает, что это свойство становится свойством класса по умолчанию. То есть:</p>
<pre class="brush:pas">
type
TStringArray = class
public
property Strings[Index: Integer]: string ...; default;
...
end;
</pre>
<p>Если у класса есть свойство по умолчанию, вы можете получить доступ к свойству через сокращенное обращение object[index], которое эквивалентно object.property[index]. То есть, объявление в предыдущем примере позволяет сократить обращение StringArray.Strings[7] до StringArray[7]. Класс может иметь только одно свойство по умолчанию с определенным списком параметров, тем не менее, разрешается перегрузка свойства по умолчанию. Изменение или перекрытие свойства по умолчанию в классах-предках может привести к непредсказуемому поведению, поскольку компилятор всегда связывает свойства статически.</p>
<br/>
<h3>Спецификаторы индекса</h3>
<p>Спецификаторы индекса позволяют нескольким свойствам использовать один и тот же метод для доступа к разным значениям. Спецификатор индекса состоит из директивы index, за которой следует целочисленная константа в диапазоне от -2147483647 до 2147483647. Если свойство имеет спецификатор индекса, в его спецификаторах чтения и записи должны присутствовать методы, а не поля. Например:</p>
<pre class="brush:pas">
type
TRectangle = class
private
FCoordinates: array[0..3] of Longint;
function GetCoordinate(Index: Integer): Longint;
procedure SetCoordinate(Index: Integer; Value: Longint);
public
property Left: Longint index 0 read GetCoordinate
write SetCoordinate;
property Top: Longint index 1 read GetCoordinate
write SetCoordinate;
property Right: Longint index 2 read GetCoordinate
write SetCoordinate;
property Bottom: Longint index 3 read GetCoordinate
write SetCoordinate;
property Coordinates[Index: Integer]: Longint
read GetCoordinate
write SetCoordinate;
...
end;
</pre>
<p>Метод для доступа со спецификатором индекса должен включать дополнительный параметр типа Integer. Для функции, указанной в спецификаторе чтения этот параметр должен идти последним. Для процедуры, указанной в спецификаторе записи, этот параметр должен идти предпоследним (предшествовать параметру для передачи значения свойства). Когда программа обращается к свойству, его целочисленная константа автоматически передается ее методу доступа.</p>
<p>Для приведенного выше объявления, если Rectangle относится к типу TRectangle, то инструкция:</p>
<pre class="brush:pas">
Rectangle.Right := Rectangle.Left + 100;
</pre>
<p>соответствует:</p>
<pre class="brush:pas">
Rectangle.SetCoordinate(2, Rectangle.GetCoordinate(0) + 100);
</pre><br/>
<h3>Спецификаторы хранения</h3>
<p>Опциональные директивы stored, default и nodefault называются спецификаторами хранения. Они не влияют на поведение программы, а отвечают за сохранение значений свойств с видимостью published в файлах хранения описания форм.</p>
<p>За директивой stored должно следовать True, False, имя поля типа Boolean, или имя метода без параметров, возвращающего значение типа Boolean. Например:</p>
<pre class="brush:pas">
property Name: TComponentName read FName write SetName stored False;
</pre>
<p>Если у свойства нет директивы stored, оно обрабатывается так же как свойство с директивой stored True.
За директивой default должна следовать константа, совпадающая по типу со свойством. Например:</p>
<pre class="brush:pas">
property Tag: Longint read FTag write FTag default 0;
</pre>
<p>Для перекрытия наследуемого значения в директиве default без указания нового значения можно воспользоваться директивой nodefault. Директивы default и nodefault поддерживаются только для порядковых типов и множеств, для которых указаны граничные элементы базового типа множества, с диапазоном значений от 0 до 31. Если свойство объявлено без указания директив default или nodefault, оно обрабатывается аналогично свойству с указанием директивы nodefault. Для значений вещественных, указательных и строковых типов неявно указываются следующие значения для директивы default: 0, nil и '' соответственно.</p>
<cite>Замечание. Запрещено использовать порядковое значение 2147483648 как значение default. Это значение зарезервировано для представления директивы nodefault.</cite>
<p>При сохранении состояния компонента, проверяются спецификаторы хранения свойств, имеющие видимость published. Если текущее значение свойства отличается от значения в директиве default (или указана директива nodefault), а для спецификатора stored установлено значение true, значение свойства сохраняется. В противном случае – нет.</p>
<cite>Замечание. Значения свойств создаваемых объектов не инициализируются автоматически значениями, указанными для директивы default. То есть директива default играет роль только в момент сохранения значений в файле, хранящем описание формы.
Спецификаторы хранения не поддерживаются для свойств-массивов. Для свойств-массивов директива default имеет другой смысл.</cite>
<br><br>
<h3>Перекрытие и повторное объявление свойств</h3>
<p>Объявление свойства, в котором не указывается тип, называется перекрытием свойства. Перекрытие свойства позволяет вам изменять наследуемую область видимости свойства или его спецификаторы. Простейшее перекрытие свойства состоит только из ключевого слова property, за которым следует идентификатор свойства. Такая форма объявления применяется для изменения области видимости свойства. Например, если класс-предок объявляет свойство с областью видимости protected, класс-потомок может объявить это свойство в секции public или published. Перекрытие свойства может включать директивы read, write,stored, default и nodefault, и любая из этих директив будет перекрывать соответствующую наследуемую директиву. Перекрытие может заменить наследуемый спецификатор доступа, добавить недостающий спецификатор или увеличить область видимости свойства, но не может удалить спецификатор доступа или уменьшить область видимости. Перекрытие может включать директиву implements, добавляющую список реализуемых интерфейсов без удаления списка наследуемых интерфейсов.</p>
<p>Следующий пример иллюстрирует применение перекрытия свойств:</p>
<pre class="brush:pas">
type
TAncestor = class
...
protected
property Size: Integer read FSize;
property Text: string read GetText write SetText;
property Color: TColor read FColor write SetColor stored False;
...
end;
type
TDerived = class(TAncestor)
...
protected
property Size write SetSize;
published
property Text;
property Color stored True default clBlue;
...
end;
</pre>
<p>Перекрытие свойства Size добавляет спецификатор записи для разрешения изменения значения свойства. Перекрытие свойств Text и Color изменяет область видимости с protected на published. Перекрытие свойства Color определяет, что значение свойства должно сохраняться в том случае, если оно не равно clBlue.</p>
<p>Повторное объявление свойств, включающее идентификатор типа, скрывает наследуемое свойство, не перекрывая его. Это значит, что новое свойство создается с таким же именем, что и наследуемое. Любое объявление свойства, содержащее идентификатор типа, должно объявляться полностью и включать в себя по крайней мере один спецификатор доступа.</p>
<p>Когда свойство скрыто или перекрыто в классе-предке, обращение к нему всегда является статическим, то есть объявленный в момент компиляции тип переменной, используемый для идентификации объекта, определяет интерпретацию идентификаторов его свойств. После выполнения следующего кода, несмотря на то, что MyObject содержит значение типа TDescendant, чтение или присваивание значения MyObject.Value запускает Method1 или Method2. Тем не менее, вы можете преобразовать тип MyObject в TDescendant для обращения к свойствам класса-потомка и их спецификаторам доступа:</p>
<pre class="brush:pas">
type
TAncestor = class
...
property Value: Integer read Method1 write Method2;
end;
TDescendant = class(TAncestor)
...
property Value: Integer read Method3 write Method4;
end;
var MyObject: TAncestor;
...
MyObject := TDescendant.Create;
</pre>
<br/>
<h3>Свойства класса</h3>
<p>К свойствам класса можно обращаться без ссылки на объект. Обращение к свойствам класса может производиться только через методы класса, объявленные как class static или через поля, объявленные как class fields. Свойство класса объявляется при помощи ключевых слов class property. Свойства класса не могут иметь область видимости published, не могут иметь инструкций stored или объявления значений default.</p>
<p>Вы можете объявить блок статических полей класса внутри объявления класса, использовав блок объявлений class var. Все поля, объявленные после ключевых слов class var имеют атрибуты статического хранения. Блок объявления class var может завершаться:</p>
<ul>
<li>Еще одним блоком объявлений class var;</li>
<li>Объявлением процедуры или функции (то есть метода) (включая процедуры класса и функции класса);</li>
<li>Объявлением свойства (включая свойства класса);</li>
<li>Объявлением конструктора или деструктора;</li>
<li>Спецификатором видимости (public, private, protected, published, strict private, and strict protected).</li>
</ul>
<p>Например:</p>
<pre class="brush:pas">
type
TMyClass = class
strict private
class var // Note fields must be declared as class fields
FRed: Integer;
FGreen: Integer;
FBlue: Integer;
public // ends the class var block
class property Red: Integer read FRed write FRed;
class property Green: Integer read FGreen write FGreen;
class property Blue: Integer read FBlue write FBlue;
end;
</pre>
<p>Обращаться к этим свойствам класса можно следующим образом:</p>
<pre class="brush:pas">
TMyClass.Red := 0;
TMyClass.Blue := 0;
TMyClass.Green := 0;
</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com1tag:blogger.com,1999:blog-1920033394599582564.post-15345280502747216472012-05-02T19:08:00.000+04:002012-05-03T03:10:45.539+04:00Методы<cite>Перевод из справочной системы Delphi. Оригинал:<a href="http://docwiki.embarcadero.com/RADStudio/en/Methods" target="_blank">Methods</a> </cite><br/>
<div align = "justify">
<p>Метод – это процедура или функция, связанная с классом. При вызове метода определяется объект (или, если это метод класса, то класс) с которым должен работать метод. Например, SomeObject.Free вызывает метод Free объекта SomeObject.</p>
<a name='more'></a><br/>
<h3>О методах</h3>
<p>Внутри объявления класса методы представлены заголовками процедур или функций, которые работают как упреждающие объявления. Где-то после объявления класса (но в этом же модуле) каждый метод должен быть реализован добавлением определяющего объявления. Например, предположим, что объявление TMyClass включает метод с именем DoSomething:</p>
<pre class="brush:pas">
type
TMyClass = class(TObject)
...
procedure DoSomething;
...
end;</pre>
<p>Определяющее объявление для DoSomething должно быть включено ниже в этом же модуле:</p>
<pre class="brush:pas">
procedure TMyClass.DoSomething;
begin
...
end;</pre>
<p>Класс может быть объявлен в модуле как в секции interface, так и в секции implementation, но определяющие объявления методов должны быть расположены в секции implementation.</p>
<p>В заголовке определяющего объявления имя метода обязательно специфицируется именем класса, к которому он принадлежит. Заголовок может повторять список параметров в из упреждающего объявления и, в таком случае, порядок, тип и имена параметров должны в точности совпадать с упреждающим объявлением. Если метод является функцией, тип возвращаемого ею результата также должен совпадать с упреждающим объявлением.</p>
<p>Объявления методов могут включать специальные директивы, которые не используются при объявлении прочих процедур и функций. Директивы должны указываться только в объявлении класса, но не в определяющем объявлении, и могут следовать только в следующем порядке:<strong> reintroduce; overload; binding; calling convention; abstract; warning</strong>, где:</p>
<ul>
<li>binding может принимать значения: virtual, dynamic или override;</li>
<li>calling convention может принимать значения: register, pascal, cdecl, stdcall или safecall; </li>
<li>warning может принимать значения: platform, deprecated или library.</li>
</ul><br>
<h4>Ключевое слово Inherited</h4>
<p>Ключевое слово inherited играет особую роль при реализации полиморфизма в поведении методов. Оно может встречаться в объявлении методов, причем иногда с указанимем идентификатора после него, а иногда – без. </p>
<p>Если за ключевым словом inherited следует имя компонента, оно обозначает обычный вызов метода, обращение к свойству или полю, за исключением того, что поиск компонента, к которому идет обращение, начинается с непосредственного предка класса, к которому относится компонент. Например, если в определяющем объявлении метода присутствует: </p>
<pre class="brush:pas">inherited Create(...);</pre>
<p>происходит вызов наследуемого конструктора Create.</p>
<p>Когда за ключевым словом inherited не следует идентификатора, обращение идет к методу с таким же именем, как вызывающий метод, или (в том случае, когда вызывающий метод является обработчиком сообщений) к наследуемому обработчику для этого же сообщения. В этом случае вызов наследуемого метода не принимает явных параметров, но при этом выполняется неявная передача параметров из вызывающего метода. Например, вызов:</p>
<pre class="brush:pas">inherited;</pre>
<p>встречается часто в реализации конструкторов. Вызывает наследуемый конструктор с таким же списком параметров (передаваемых наследуемому конструктору).</p>
<br>
<h4>Идентификатор Self</h4>
<p>При реализации метода идентификатор Self обращается к объекту, из которого происходит вызов метода. Для примера приведем реализацию метода Add класса TCollection, объявленного в модуле Classes:</p>
<pre class="brush:pas">
function TCollection.Add: TCollectionItem;
begin
Result := FItemClass.Create(Self);
end;</pre>
<p>Метод Add вызывает метод Create класса, на который ссылается поле FitemClass (который всегда является наследником TCollectionItem). TCollectionItem.Create принимает единственный параметр типа TСollection. Таким образом, метод Add передает экземпляр объекта типа TCollection, вызывающего его. Это аналогично коду:</p>
<pre class="brush:pas">
var MyCollection: TCollection;
...
MyCollection.Add // MyCollection передается в
// метод TCollectionItem.Create</pre>
<p>Self полезен по самым различным причинам. Например, если идентификатор компонента объявлен в классе, он может быть объявлен повторно в блоке метода, относящегося к этому классу. В этом случае, вы можете получить доступ к идентификатору компонента, используя вызов Self.Identifier.</p>
<br>
<h3>Связывание методов</h3>
<p>Связывание методов может быть статическим (static) (по умолчанию), виртуальным (virtual) или динамическим (dynamic). Виртуальные и динамические методы могут перекрываться и они могут быть абстрактными. Это подразделение методов начинает иметь значение, когда переменная одного типа класса хранит значение типа класса-потомка. Оно определяет, какая реализация метода будет активирована при запуске метода.</p>
<br>
<h4>Статические методы</h4>
<p>Методы по умолчанию являются статическими. При вызове статического метода, объявленный при компиляции тип класса или объектная переменная, использованные при вызове метода определяют, какая из реализаций метода активируется. В следующем примере метод Draw является статическим:</p>
<pre class="brush:pas">
type
TFigure = class
procedure Draw;
end;
TRectangle = class(TFigure)
procedure Draw;
end;</pre>
<p>При таком объявлении приведенный ниже код иллюстрирует вызов статического метода. Во втором вызове Figure.Draw, переменная Figure ссылается на класс TRectangle, но вызов метода активирует реализацию метода Draw в классе TFigure, поскольку объявленный тип переменной Figure – это TFigure:</p>
<pre class="brush:pas">
var
Figure: TFigure;
Rectangle: TRectangle;
begin
Figure := TFigure.Create;
Figure.Draw; // calls TFigure.Draw
Figure.Destroy;
Figure := TRectangle.Create;
Figure.Draw; // calls TFigure.Draw
TRectangle(Figure).Draw; // calls TRectangle.Draw
Figure.Destroy;
Rectangle := TRectangle.Create;
Rectangle.Draw; // calls TRectangle.Draw
Rectangle.Destroy;
end;
</pre>
<br>
<h4>Виртуальные и динамические методы</h4>
<p>Для того, чтобы сделать метод виртуальным или динамическим необходимо включить в его объявление соответствующую директиву (virtual или dynamic). Виртуальные и динамические методы, в отличии от статических могут быть перекрыты в классе-предке. При вызове перекрытого метода, решение о том, какую реализацию метода активировать, принимает действительный (определенный в процессе работы программы) тип класса или объекта, использванный при вызове метода.</p>
<p>Чтобы перекрыть метод его следует объявить повторно с указанием директивы override. Перекрывающее объявление должно совпадать с объявлением в классе-предке по порядку и типу параметров, а так же типу возвращаемого результата (для функций).
В следующем примере метод Draw, объявленный в классе TFigure перекрывается в двух классах предках:</p>
<pre class="brush:pas">
type
TFigure = class
procedure Draw; virtual;
end;
TRectangle = class(TFigure)
procedure Draw; override;
end;
TEllipse = class(TFigure)
procedure Draw; override;
end;</pre>
<p>При таком объявлении нижеприведенный код показывает результат вызова виртуального метода при помощи переменной, чей тип меняется в процессе выполнения программы:</p>
<pre class="brush:pas">
var
Figure: TFigure;
begin
Figure := TRectangle.Create;
Figure.Draw; // calls TRectangle.Draw
Figure.Destroy;
Figure := TEllipse.Create;
Figure.Draw; // calls TEllipse.Draw
Figure.Destroy;
end;</pre>
<p>Только виртуальные и динамические методы могут быть перекрыты. Тем не менее, любые методы могут быть перегружены.
Компилятор Delphi так же поддерживает концепцию финального виртуального метода. Если при объявлении виртуального метода было использовано ключевое слово final, классы-предки не смогут перекрывать его. Применение ключевого слова final может быть хорошим инженерным решением, которое может помочь документировать планируемое применение класса. Кроме того, оно помогает компилятору оптимизировать код.</p>
<br>
<h5>Виртуальный или динамический?</h5>
<p>В Delphi для Win32, виртуальные и динамические методы семантически одинаковы. Тем не менее, они отличаются в реализации обработки вызовов методов при выполнении программы. Виртуальные методы оптимизированы с точки зрения скорости, а динамические - снижают размер результирующего кода.</p>
<p>В целом виртуальные методы являются наиболее эффективным способом реализации полиморфизма в поведении объектов. Динамические методы полезны в том случае, когда базовый класс объявляет множество перекрываемых методов, которые наследуются большим количеством классов-потомков, но при этом перекрываются нечасто.</p>
<cite>Замечание: Динамические методы следует использовать только в тех случаях, когда выгода от их применения очевидна. Во всех обычных ситуациях следует использовать виртуальные методы.</cite>
<br>
<h5>Скрывать или перекрывать?</h5>
<p>Если объявление метода определяет такой же идентификатор и набор параметров, какой уже присутствует в объявлении метода в классе-предке, но при этом не использует директиву override, новое объявление просто скрывает наследуемый метод, не перекрывая его. Оба метода будут существовать в классе-предке, в котором имя метода связано статически. Например:</p>
<pre class="brush:pas">
type
T1 = class(TObject)
procedure Act; virtual;
end;
T2 = class(T1)
procedure Act; // Act is redeclared, but not overridden
end;
var
SomeObject: T1;
begin
SomeObject := T2.Create;
SomeObject.Act; // calls T1.Act
end;</pre>
<br>
<h5>Директива Reintroduce</h5>
<p>Директива reintroduce подавляет предупреждения компилятора о сокрытии объявленных ранее виртуальных методов. Например:</p>
<pre class="brush:pas">
procedure DoSomething; reintroduce; // класс-предок также
// имеет метод DoSomething</pre>
<p>Директиву reintroduce следует использовать, когда вы хотите скрыть наследуемый виртуальный метод, заменив его новым методом.</p>
<br>
<h5>Абстрактные методы</h5>
<p>Абстрактные методы – это виртуальные или динамические методы, не имеющие реализации в том классе, где они объявлены. Они должны быть реализованы в классе-потомке. Абстрактные методы должны объявляться с использованием директивы abstract, следующей за директивой virtual или dynamic. Например:</p>
<pre class="brush:pas">
procedure DoSomething; virtual; abstract;</pre>
<p>Вы можете вызывать абстрактный метод только в классе или экземпляре, перекрывающем этот метод. </p>
<br>
<h3>Методы класса</h3>
<p>Большая часть методов называются методами экземпляров, поскольку они работают с отдельными экземплярами объекта. Метод класса – это метод (отличный от конструктора), который оперирует классами, но не объектами. Существует два типа методов класса: обычные и статические.</p>
<br>
<h4>Обычные методы класса</h4>
<p>Объявление метода класса должно начинаться с зарезервированного слова class. Например:</p>
<pre class="brush:pas">
type
TFigure = class
public
class function Supports(Operation: string): Boolean; virtual;
class procedure GetInfo(var Info: TFigureInfo); virtual;
...
end;</pre>
<p>Определяющее объявление метода класса также начинается с зарезервированного слова class:</p>
<pre class="brush:pas">
class procedure TFigure.GetInfo(var Info: TFigureInfo);
begin
...
end;</pre>
<p>В определяющем объявлении метода класса идентификатор Self представляет класс, в котором вызывается метод (может быть классом-потомком, по отношению к классу, в котором объявлен метод). Если методы вызывается в классе С, Self имеет тип class of C.</p> <p>Таким образом, вы не сможете использовать Self для доступа к полям, свойствам или обычным методам экземпляра. Вы можете использовать Self для вызова конструкторов, прочих методов класса или для доступа к свойствам или полям класса.
Метод класса может быть вызван при помощи ссылки на класс или объект. При вызове через ссылку на объект, класс объекта принимает значение Self.</p>
<br>
<h4>Статические методы класса</h4>
<p>Как и обычные методы класса, статические методы могут быть доступны без обращения к экземпляру объекта. В отличии от обычных методов, статические методы совсем не имеют параметра Self. Они также не могут получать к компонентам экземпляра (у них есть доступ к полям, свойствам и методам класса). Кроме того, они, также не могут быть виртуальными.</p>
<p>Для объявления статического метода класса следует добавить ключевое слово класса к объявлению:</p>
<pre class="brush:pas">
type
TMyClass = class
strict private
class var
FX: Integer;
strict protected
// Замечание: компоненты, получающие доступ к свойствам класса
// должны быть объявлены как class static.
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;
public
class property X: Integer read GetX write SetX;
class procedure StatProc(s: String); static;
end;</pre>
<p>Как и обычные методы класса, статические методы могут вызываться при помощи типа класса (без ссылки на экземпляр класса):</p>
<pre class="brush:pas">
TMyClass.X := 17;
TMyClass.StatProc('Hello');</pre>
<br>
<h3>Перегрузка методов</h3>
<p>Метод может быть объявлен повторно с указанием директивы overload. В этом случае объявленный повторно метод имеет набор параметров, отличный от объявленного в классе-предке, и перегружает наследуемый метод, не скрывая его. Вызов метод в классе-потомке активирует реализацию метода с параметрами, совпадающими с фактическими.</p>
<p>Если вы перегружаете виртуальный метод, - пользуйтесь директивой reintroduce:</p>
<pre class="brush:pas">
type
T1 = class(TObject)
procedure Test(I: Integer); overload; virtual;
end;
T2 = class(T1)
procedure Test(S: string); reintroduce; overload;
end;
...
SomeObject := T2.Create;
SomeObject.Test('Hello!'); // вызывает T2.Test
SomeObject.Test(7); // вызывает T1.Test</pre>
<p>Внутри класса вы не можете установить видимость перегружаемых методов с одинаковыми именами в published. Обработка информации о типах в режиме выполнения программы требует уникального имени для компонентов с такой спецификацией видимости.</p>
<pre class="brush:pas">
type
TSomeClass = class
published
function Func(P: Integer): Integer;
function Func(P: Boolean): Integer; // ошибка
...</pre>
<p>Методы, служащие для спецификации доступа к значению свойств (read или write) не могут быть перегружены.
Реализация перегружаемых методов должна повторять список параметров, содержащихся в объявлении класса. </p>
<br>
<h3>Конструкторы</h3>
<p>Конструктор – это особый метод, который создает и инициализирует объекты. Объявление конструктора выглядит как объявление обычной процедуры, но начинается с ключевого слова constructor. Примеры:</p>
<pre class="brush:pas">
constructor Create;
constructor Create(AOwner: TComponent);
</pre>
<p>Конструкторы должны использовать конвенцию вызова register. При том, что в объявлении конструктора не указывается возвращаемого значения, конструктор возвращает ссылку на создаваемый или вызывающий его объект.</p>
<p>Класс, может иметь более, чем один конструктор, но большая часть классов имеет только один. Обычно для конструкторов используется имя Create.</p>
<p>Для создания объекта следует вызывать конструктор, специфицируя название типа класса. Например:</p>
<pre class="brush:pas">
MyObject := TMyClass.Create;</pre>
<p>Эта инструкция выделяет память для нового объекта, устанавливает значения для всех целочисленных полей в 0, присваивает значение nil для всех полей указательного типа и устанавливает пустое значение для всех строковых полей. Прочие операции, указанные в реализации конструктора, выполняются позже. Обычно объекты инициализируются значениями, которые передаются как параметры в конструктор. Наконец, конструктор возвращает ссылку на только что созданный и инициализированный объект. Тип возвращаемого значения совпадает с типом, указанным при вызове конструктора.</p>
<p>Если при выполнении конструктора, вызванного инструкцией с указанием ссылки на класс, возникает исключение деструктор Destroy вызывается автоматически для разрушения незаконченного объекта.</p>
<p>Когда конструктор вызывается инструкцией с указанием ссылки на объект, он не создает объект, а работает с вызывающим его объектом, выполняя только инструкции, указанные в его реализации, а затем возвращает ссылку на объект. Конструктор обычно вызывается инструкцией со ссылкой на объект в сочетании с зарезервированным словом inherited для выполнения наследуемого конструктора.</p>
<p>Далее приведен пример класса и его конструктора:</p>
<pre class="brush:pas">
type
TShape = class(TGraphicControl)
private
FPen: TPen;
FBrush: TBrush;
procedure PenChanged(Sender: TObject);
procedure BrushChanged(Sender: TObject);
public
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
...
end;
constructor TShape.Create(Owner: TComponent);
begin
inherited Create(Owner); // Initialize inherited parts
Width := 65; // Change inherited properties
Height := 65;
FPen := TPen.Create; // Initialize new fields
FPen.OnChange := PenChanged;
FBrush := TBrush.Create;
FBrush.OnChange := BrushChanged;
end;</pre>
<p>Первой инструкцией в конструкторе обычно бывает вызов наследуемого конструктора для инициализации унаследованных полей. Затем конструктор инициализирует поля, объявленные в классе-потомке. Поскольку конструктор всегда очищает память, которую он выделяет для создания объекта, все поля порядковых типов принимают значение 0, указатели и переменные типа класс – nil, строковые типы инициализируются пустым значением, а вариантные типы – значением Unassigned. Таким образом нет необходимости в теле конструктора инициализировать поля значениями, отличными от ноля или пустых значений.</p>
<p>При вызове через идентификатор типа класса конструктор, объявленный как virtual, ведет себя так же как обычный статический конструктор. Тем не менее, при вызове через ссылку на класс виртуальные конструкторы допускают полиморфное создание объектов – то есть создание объектов, тип которых неизвестен на этапе компиляции.</p>
<br>
<h3>Деструкторы</h3>
<p>Деструктор – это особый метод, который при вызове разрушает объект и освобождает занимаемую им память. Объявление деструктора выглядит как объявление обычной процедуры, но начинается с ключевого слова destructor. Например:</p>
<pre class="brush:pas">
destructor SpecialDestructor(SaveData: Boolean);
destructor Destroy; override;
</pre>
<p>Деструкторы на платформе Win32 используют по умолчанию конвенцию вызова register. Хоть класс и может иметь несколько деструкторов, рекомендуется перекрывать наследуемый метод Destroy, без объявления прочих деструкторов.</p>
<p>Для вызова деструктора вы должны специфицировать экземпляр объекта:</p>
<pre class="brush:pas">
MyObject.Destroy;
</pre>
<p>При вызове деструктора вначале выполняются инструкции, указанные в его теле. Обычно это уничтожение встроенных объектов и высвобождение ресурсов, выделенных для разрушаемого объекта. После чего память, выделенная для объекта освобождается.</p>
<p>Далее приведен пример реализации деструктора:
<pre class="brush:pas">
destructor TShape.Destroy;
begin
FBrush.Free;
FPen.Free;
inherited Destroy;
end;</pre>
<p>Последней инструкцией в теле деструктора обычно бывает вызов наследуемого деструктора, которые уничтожает наследуемые поля.
При возникновении исключительной ситуации в процессе создании объекта, для разрушения незаконченного объекта автоматически вызывается деструктор Destroy. Это обозначает, что деструктор Destroy должен быть подготовлен к разрушению частично созданных объектов. Поскольку конструктор устанавливает нулевые и пустые значения полей перед выполнением каких-либо других действий, поля типа класс и указатели в частично созданном объекте всегда имеют значение nil. Прежде чем работать с такими полями деструктор должен проверять их на наличие этого значения. Вызов метода Free (объявлен в TObject) – это более подходящий способ для разрушения объекта с предварительной проверкой значений его полей.</p>
<br>
<h3>Конструкторы классов</h3>
<p>Конструктор класса – это особый метод класса, который недосупен разработчикам. Вызовы конструкторов автоматически вставляются компилятором в код секции инициализации модуля, в котором объявлен класс. Обычно конструкторы классов применяются для инициализации статических полей класса или для выполнения особого типа инициализации, требуемой для того, чтобы класс или какой-либо его экземпляр функционировал корректно. Даже если такой же результат может быть достигнут размещением кода инициализации класса в секции initialization, конструкторы класса в большей степени помогают компилятору решить, какой именно класс должен быть включен в результирующий двоичный файл, а какой – удален из него.</p>
<p>Следующий пример показывает обычную инициализацию полей класса:</p>
<pre class="brush:pas">
type
TBox = class
private
class var FList: TList<Integer>;
end;
implementation
initialization
{ Initialize the static FList member }
TBox.FList := TList<Integer>.Create();
end.</pre>
<p>Этот способ плох тем, что приложение включает модуль, объявляющий TBox, даже в том случае, если этот модуль на самом деле не используется им. В приведенном примере класс TBox включается в результирующий бинарный файл, поскольку он упоминается в секции инициализации. Чтобы исправить ситуацию, рассмотрим применение конструкторов класса:</p>
<pre class="brush:pas">
type
TBox = class
private
class var FList: TList<Integer>;
class constructor Create;
end;
implementation
class constructor TBox.Create;
begin
{ Initialize the static FList member }
FList := TList<Integer>.Create();
end;
end.</pre>
<p>В этом случае, компилятор проверяет, используется ли где-либо в приложении класс TBox, и, если это так, вызов конструктора класса автоматически добавляется в секцию инициализации модуля.</p>
<cite>Замечание: Несмотря на то, что компилятор уделяет внимание порядку инициализации классов, в некоторых сложных ситуациях он может оказаться случайным. Это происходит, когда конструктор класса зависит от состояния другого класса, который, в свою очередь, зависит от первого.</cite><br>
<cite>Замечание: конструктор класса-дженерика или записи может быть выполнен несколько раз. Точное количество запусков конструктора зависит от количества специализированных версий типа-дженерика. Например, конструктор класса для специализированного класса TList<String> может быть выполнен несколько раз в одном и том же приложении.</cite>
<br><br>
<h3>Деструкторы классов</h3>
<p>Деструкторы классов выполняют финализацию класса. Деструкторы класса обладают тем же преимуществом, что и конструкторы класса.
Следующий пример построен на предыдущем (о конструкторах класса) и объявляет финализирующую подпрограмму: </p>
<pre class="brush:pas">
type
TBox = class
private
class var FList: TList<Integer>;
class constructor Create;
class destructor Destroy;
end;
implementation
class constructor TBox.Create;
begin
{ Initialize the static FList member }
FList := TList<Integer>.Create();
end;
class destructor TBox.Destroy;
begin
{ Finalize the static FList member }
FList.Free;
end;
end.</pre>
<cite>Замечание: деструктор класса-дженерика или записи может быть выполнен несколько раз. Точное количество запусков деструктора зависит от количества специализированных версий типа-дженерика. Например, деструктор класса для специализированного класса TList<String> может быть выполнен несколько раз в одном и том же приложении.</cite>
<br><br>
<h3>Методы-обработчики сообщений</h3>
<p>Обработчики сообщений реализуют реакцию на динамически передаваемые сообщения. Синтаксис метода-обработчика сообщения поддерживается для всех платформ. Приложения VCL применяют обработчики сообщений для реагирования на сообщения Windows. </p>
<p>Обработчик сообщения объявляется при помощи директивы message, за которой следует целочисленная константа с диапазоном значений от 1 до 49151, представляющая собой идентификатор сообщения. Для обработчиков сообщений в элементах управления VCL эта константа может быть идентификатором одного из сообщений Win32, определенных в модуле Messages. Метод-обработчик сообщения должен быть процедурой с единственным параметром, передаваемым по ссылке.</p>
<p>Пример:</p>
<pre class="brush:pas">
type
TTextBox = class(TCustomControl)
private
procedure WMChar(var Message: TWMChar); message WM_CHAR;
...
end;</pre>
<p>Обработчик сообщения не должен содержать директивы override для переопределения наследуемого метода. На самом деле он даже не должен повторять имени или типа параметра перекрываемого метода. Только идентификатор сообщения определяет, какое сообщение должен обработать метод и является ли он перекрывающим методом. </p>
<br>
<h4>Реализация методов-обработчиков сообщений</h4>
<p>Реализация обработчика может вызывать наследуемый метод-обработчик. Например:</p>
<pre class="brush:pas">
procedure TTextBox.WMChar(var Message: TWMChar);
begin
if Message.CharCode = Ord(#13) then
ProcessEnter
else
inherited;
end;</pre>
<p>Инструкция inherited выполняет поиск по иерархии класса и подключает первый найденный метод с соответствующим идентификатором сообщения и передает ему запись сообщения. Если классы-предки не реализуют метод для обработки сообщения с данным идентификатором, директива inherited вызывает метод DefaultHandler , объявленный в TObject.</p>
<p>Реализация DefaultHandler в TObject не подразумевает выполнения каких-либо операций. Перекрывая DefaultHandler, класс может реализовать собственную обработку сообщений по умолчанию. Для элементов управления на платформе Win32, метод DefaultHandler вызывает Win32 API DefWindowProc.</p>
<br>
<h4>Обработка сообщений</h4>
<p>Обработчики сообщений редко вызываются явно. Обычно сообщения передаются объекту при помощи метода Dispatch , наследуемого от TObject:</p>
<pre class="brush:pas">
procedure Dispatch(var Message);
</pre>
<p>Параметр Message передаваемый методу Dispatch должен быть записью, первым полем которой должно быть поле типа Word, содержащее идентификатор сообщения.</p>
<p>Dispatch выполняет поиск обработчика сообщения в иерархии классов (начиная с класса объекта, в котором был вызван этот метод) и запускает первый найденный обработчик для идентификатора, который был ему передан. Если обработчика для сообщения с данным идентификатором не находится, Dispatch вызывает DefaultHandler.</p>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com2tag:blogger.com,1999:blog-1920033394599582564.post-81747476878007958442012-04-28T21:43:00.000+04:002012-04-30T23:57:42.443+04:00Примеры простых программ на Lazarus под Win32 (Часть 2)<div align = "justify">
<p>В этом посте я расскажу о своем первом опыте по работе с базами данных в Lazarus.</p>
<p>Как я уже писал в первой части, задача была подключиться к удаленной базе данных MySQL, извлечь оттуда данные и записать их в CSV. </p>
<p>Так наверное довольно часто бывает, когда берешься за новый инструмент, все может пойти совсем не так, как было задумано изначально. И хоть инструмент не совсем новый, и задача - ну уж совсем понятная, мне все-таки пришлось поломать голову в попытках разыскать решения для совсем банальных проблем...</p><a name='more'></a><br/>
<p>Для начала я убедился, что я в принципе могу подключиться к целевой базе данных с имеющимися реквизитами доступа. Для этого воспользовался клиентом <a href="http://www.devart.com/ru/dbforge/mysql/studio/" target="_blank">dbForge Studio for MySQL</a>. Подключение прошло вполне себе успешно.</p>
<p>Далее я создал проект Lazarus, на пустую форму бросил стандартный компонент TMySQL51Connection, задал его свойства и попробовал подключиться к базе данных. И получил ошибку </p>
<p><strong>"TMySQL51Connection can not work with the installed MySQL client version: Expected (5.1), got (6.0.0)". </strong></p>
<p>Я испробовал целую кучу клиентских библиотек самых разных версий, копировал их в самые неожиданные места (ну, коль скоро их присутствие в папке проекта никакой роли не сыграло), но подключения так и не случилось. Оговорюсь на всякий случай, что MySQL на моей машине не установлен. Промучившись со стандартными компонентами и перерыв форумы на предмет решения, я подумал, что с меня довольно, и надо искать альтернативу. </p>
<p>Надо сказать, что альтернатива нашлась довольно быстро в виде библиотеки <a href="http://wiki.freepascal.org/ZeosDBO" target="_blank">ZeosLib</a>, которая установилась сразу и без проблем заработала. После замены TMySQL51Connection на TZConnection, дело пошло вполне себе споро и уложилось в 140 строк размашистым почерком:</p>
<pre class="brush:pas">
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, mysql50conn, mysql51conn, FileUtil, Forms, Controls, ExtCtrls,
Graphics, Dialogs, StdCtrls, ZConnection, ZDataset;
type
{ TForm1 }
TForm1 = class(TForm)
procedure bStartClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
var
mmInfo: TMemo;
pnTop: TPanel;
bStart: TButton;
ZConn: TZConnection;
ZROQuery: TZReadOnlyQuery;
{$R *.lfm}
{ TForm1 }
procedure ShowInfo(const S: string; const Arr: array of const);
begin
mmInfo.Lines.Add(
TimeToStr(Now) + ' ' + Format(S, Arr));
end;
procedure TForm1.bStartClick(Sender: TObject);
var
SL: TStringList;
Cnt: integer;
FN: string;
begin
SL := TStringList.Create;
Cnt := 0;
try
ShowInfo('Подключение к удаленной базе данных...', []);
if ZConn.Connected then
ZConn.Disconnect;
ZConn.Connect;
ShowInfo('Выполнено.', []);
ShowInfo('Выполняется запрос...', []);
ZROQuery.Active := True;
SL.Add(
'id_company;company;category_fi;province;city;address;' + 'phone;url;email;');
while not ZROQuery.EOF do
begin
SL.Add(
ZROQuery.FieldByName('id_company').AsString + ';' +
ZROQuery.FieldByName('company').AsString + ';' +
ZROQuery.FieldByName('category_fi').AsString + ';' +
ZROQuery.FieldByName('province').AsString + ';' +
ZROQuery.FieldByName('city').AsString + ';' +
ZROQuery.FieldByName('address').AsString + ';' +
ZROQuery.FieldByName('phone').AsString + ';' +
ZROQuery.FieldByName('url').AsString + ';' +
ZROQuery.FieldByName('email').AsString + ';'
);
Inc(Cnt);
if Cnt mod 100 = 0 then
ShowInfo('получено %d строк', [Cnt]);
ZROQuery.Next;
end;
ZROQuery.Close;
ShowInfo('Завершено. Всего получено строк: %d', [Cnt]);
FN := ExtractFilePath(Application.ExeName) + 'data.csv';
SL.SaveToFile(FN);
ShowInfo('Результаты сохранены в файл %s', [FN]);
except
on E: Exception do
mmInfo.Lines.Add(E.Message);
end;
FreeAndNil(SL);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
pnTop := TPanel.Create(Self);
pnTop.Parent := Self;
pnTop.Align := alTop;
mmInfo := TMemo.Create(Self);
mmInfo.Parent := Self;
mmInfo.Align := alClient;
bStart := TButton.Create(pnTop);
bStart.Parent := pnTop;
bStart.Caption := 'Start';
bStart.OnClick := @bStartClick;
ZConn := TZConnection.Create(Self);
ZConn.HostName := 'some_host.info';
ZConn.User := 'some_user';
ZConn.Password := 'SoMePwd1';
ZConn.Database := 'some_db';
ZConn.Protocol := 'mysql-5';
ZConn.Port:=3306;
ZConn.AutoCommit := True;
ZROQuery := TZReadOnlyQuery.Create(Self);
ZROQuery.Connection := ZConn;
ZROQuery.SQL.Text :=
'select c.ID_COMPANY, c.COMPANY, ca.CATEGORY_RU,ca.CATEGORY_FI, ' +
' p.PROVINCE, ci.CITY, ' + ' c.ADDRESS, a.PHONE,a.URL,c.EMAIL,' +
' a.ID_ADVERT_TYPE,a.IS_COLORED,a.SEND2TOP,a.SERVE_LANGS ' +
' from adverts a ' + ' left JOIN companies c on c.ID_COMPANY=a.ID_COMPANY' +
' left join cities ci on ci.ID_CITY=c.ID_CITY' +
' left join provinces p on p.ID_PROVINCE=ci.ID_PROVINCE' +
' left join categories ca on ca.ID_CATEGORY=a.ID_CATEGORY';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if ZConn.Connected then
ZConn.Disconnect;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
bStart.Top := 10;
bStart.Left := Form1.Width - bStart.Width - 10;
end;
end.
</pre>
<p>
Думаю, что в особых пояснениях приведенный код не нуждается, поскольку листинг вполне заменяет комментарии в коде, да и вообще... эта программка не сильно отличается от предыдущей.</p>
<p>На мой взгляд, важный момент здесь – это неявное управление транзакциями, которое обусловлено установкой флага </p>
<pre class="brush:pas"> ZConn.AutoCommit := True;</pre>
<p>Неявное управление транзакциями подразумевает, что каждый выполняемый приложением запрос инициирует транзакцию, которая завершается автоматически после выполнения запроса. Однако дело в том, что TZConnection, имеет довольно оригинальную логику функционирования, о которой имхо необходимо упомянуть:</p>
<p>Далее привожу перевод поста на эту тему с <a href="http://lazarus.freepascal.org/index.php?topic=12299.0" target="_blank">lazarus.freepascal.org</a>:
<cite><p>На самом деле свойство AutoCommit работает следующим образом:</p>
<p>Когда AutoCommit имеет значение true, после выполнения каждого запроса транзакции подтверждаются в автоматическом режиме. Чтобы избежать автоматического подтверждения транзакции, вы можете явно вызвать инструкцию StartTransaction. В этом случае транзакция будет завершена после явного вызова Commit.</p>
<p>Когла AutoCommit имеет значение false вы не должны вызывать StartTransaction. То есть транзакции стартуют автоматически, но не завершаются после выполнения каждой инструкции.</p></cite>
<p>Перевод документации по StartTransaction c <a href="http://zeos.firmos.at/kb.php?mode=article&k=6" target="_blank">zeos.firmos.at:</a></p>
<cite><p><strong>procedure StartTransaction</strong>: В подключенной базе данных процедура StartTransaction стартует новую транзакцию. Эта инструкция может быть использована, только если свойство AutoCommit имеет значение TRUE. В противном случае при вызове StartTransaction будет создаваться исключительная ситуация SInvalidOpInNonAutoCommit. Такая логика работы обуславливается тем, что StartTransaction предназначена для корректировки поведения приложения в режиме AutoCommit. При вызове StartTransaction, режим AutoCommit выключается, затем после вызова Commit или Rollback, режим AutoCommit включается снова.</p>
<p>Если значение свойства AutoCommit установлено в false, новые транзакции создаются автоматически, а вы можете принимать решение, как они должны быть завершены (Commit и Rollback).</p></cite>
<p>Короче говоря, вопрос управления транзакциями – это тема для некоторого изучения и экспериментов. Чуть попозже я думаю провести их и разместить здесь результаты.</p>
<p>До новых встреч в эфире;-)</p>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com2tag:blogger.com,1999:blog-1920033394599582564.post-82284977601675192412012-03-25T22:16:00.000+04:002012-03-25T22:18:41.394+04:00Анонимные методы в Delphi<cite>Перевод из справочной системы Delphi</cite><br/>
<div align = "justify">
<p>Как предполагает название, анонимный метод – это процедура или функция, с которой не связано имени. Анонимный метод обрабатывает блок кода как сущность, которая может быть присвоена переменной или использоваться в качестве параметра для передачи в другой метод. Вдобавок анонимный метод может ссылаться на переменные и связывать значения с переменными в контексте, где он объявлен. Анонимные методы могут объявляться и использоваться при помощи простого синтаксиса. Они схожи с замыканиями, определенными в других языках программирования.</p>
<a name='more'></a><br/>
<h3>Синтаксис</h3>
<p>Анонимный метод определяется аналогично обычной процедуре или функции, только без имени. Например, эта функция возвращает функцию, определенную как анонимный метод:</p>
<pre class="brush:pas">function MakeAdder(y: Integer): TFuncOfInt;
begin
Result := { начало анонимного метода } function(x: Integer) : Integer
begin
Result := x + y;
end; { конец анонимного метода }
end;</pre>
<p>Функция MakeAdder возвращает фунцию, которую объявляет без имени как анонимный метод.</p>
<p>Следует учесть, что MakeAdder возвращает значение типа TFuncOfInt. Тип анонимного метода объявлен как ссылка на метод:</p>
<pre class="brush:pas">type
TFuncOfInt = reference to function(x: Integer): Integer;</pre>
Это объявление указывает, что анонимный метод:
<ul>
<li>Является функцией;</li>
<li>Принимает целочисленный параметр;</li>
<li>Возвращает целочисленное значение.</li>
</ul>
Объявление типа для анонимной процедуры или функции:
<pre class="brush:pas">type
TType1 = reference to procedure (parameterlist);
TType2 = reference to function (parameterlist): returntype;</pre>
где (parameterlist) является опциональным.
Далее приведена еще пара примеров типов:
<pre class="brush:pas">type
TSimpleProcedure = reference to procedure;
TSimpleFunction = reference to function(x: string): Integer; </pre>
Анонимный метод объявлен как процедура или функция без имени:
<pre class="brush:pas">// процедура
procedure (parameters)
begin
{ блок инструкций }
end;
// функция
function (parameters): returntype
begin
{ блок инструкций }
end;</pre>
<p>где (parameters) является опциональным.</p>
<br>
<h3>Применение анонимных методов</h3>
<p>Обычно анонимные методы присваивают чему-либо, как это показано в примерах:</p>
<pre class="brush:pas">myFunc := function(x: Integer): string
begin
Result := IntToStr(x);
end;
myProc := procedure(x: Integer)
begin
Writeln(x);
end;</pre>
<p>Анонимные методы могут также возвращаться функциями или передаваться в качестве параметров при вызове методов. Далее в качестве примера приведено применение переменной анонимного метода myFunc, объявленной выше:</p>
<pre class="brush:pas">type
TFuncOfIntToString = reference to function(x: Integer): string;
procedure AnalyzeFunction(proc: TFuncOfIntToString);
begin
{ какой-то код }
end;
// вызов процедуры с параметром - анонимным методом
// через переменную:
AnalyzeFunction(myFunc);
// использование анонимного метода напрямую:
AnalyzeFunction(function(x: Integer): string
begin
Result := IntToStr(x);
end;)</pre>
<p>Ссылки на методы могут присваиваться как обычным методам, так и анонимным. Например:</p>
<pre class="brush:pas">
type
TMethRef = reference to procedure(x: Integer);
TMyClass = class
procedure Method(x: Integer);
end;
var
m: TMethRef;
i: TMyClass;
begin
// ...
m := i.Method; //присваивание значения ссылке на метод
end;</pre>
<p>Тем не менее, обратное утверждение неверно: вы не можете присвоить значение типа анонимного метода обычному указателю на метод. Ссылки на методы являются управляемыми типами, а указатели на методы – неуправляемыми. То есть для обеспечения безопасности работы с типами присваивание ссылок на методы указателям на методы не поддерживается. Например, события – это свойства-указатели на методы, и вы не сможете использовать анонимные методы для работы с событиями. Для получения более подробной информации по этому ограничению – смотрите секцию о привязке переменных.</p>
<br>
<h3>Привязка переменных в анонимных методах</h3>
<p>Ключевая особенность анонимных методов заключается в том, что они могут обращаться к переменным, доступным в их области видимости. Более того, эти переменные могут быть привязаны к своим значениям и обернуты обращением к анонимному методу, что фиксирует состояние переменных и удлиняет время их существования.</p>
<br>
<h4>Иллюстрация привязки переменных</h4>
Рассмотрим еще раз объявленную выше функцию:
<pre class="brush:pas">function MakeAdder(y: Integer): TFuncOfInt;
begin
Result := function(x: Integer): Integer
begin
Result := x + y;
end;
end; </pre>
<p>Мы можем создать экземпляр этой функции, который привязывает значение переменной:</p>
<pre class="brush:pas">var
adder: TFuncOfInt;
begin
adder := MakeAdder(20);
Writeln(adder(22)); // выводит 42
end.</pre>
<p>Переменная adder хранит анонимный метод, который привязывае значение 20 к переменной y, к которой обращается блок кода анонимного метода. Эта привязка сохраняется даже в том случае, когда значение пропадает из области видимости.</p>
<br>
<h4>Анонимные методы как события</h4>
<p>Причина для использования ссылок на методы – возможность иметь тип, содержащий привязанные значения, которые также известные как значения замыкания. Поскольку замыкания закрывают контекст, в котором они определены, включая любые локальные переменные, на которые идет ссылка в точке объявления, у них наступает состояние, в котором их память должна быть освобождена. Ссылки на методы являются управляемыми типами (работают по принципу подсчета ссылок), они могут следить за этим состоянием и освобождать память, когда это необходимо. Если ссылка на метод или замыкание могли бы свободно присваиваться указателю на метод (например, событию), это привело бы к "подвисанию указателей" или утечкам памяти.</p>
<p>События в Delphi – это конвенция для свойств. Между событием и свойством нет разницы, кроме как в типе. Если свойство связано с указательным типом, оно является событием.</p>
<p>Если свойство имеет тип ссылки на метод, то с точки зрения логики, оно также должно рассматриваться как событие. Тем не менее, IDE не обрабатывает его как событие. Это утверждение относится к классам, установленным в IDE в качестве компонентов или элементов управления. </p>
<p>Следовательно, чтобы иметь событие в компоненте или элементе управления, допускающее присвоение значения с использованием ссылки на метод или замыкания, свойство должно иметь тип ссылки на метод. Что, однако, неудобно, поскольку IDE не распознает его как событие.</p>
<p>Далее приведен пример использования свойств типа ссылок на метод, который работает аналогично событию:</p>
<pre class="brush:pas">type
TProc = reference to procedure;
TMyComponent = class(TComponent)
private
FMyEvent: TProc;
public
// свойство MyEvent работает как событие:
property MyEvent: TProc read FMyEvent write FMyEvent;
// прочий код вызывает FMyEvent как обычное событие
end;
...
var
c: TMyComponent;
begin
c := TMyComponent.Create(Self);
c.MyEvent := procedure
begin
ShowMessage('Hello World!'); // показывается, когда
//TMyComponent вызывает MyEvent
end;
end;
</pre>
<br>
<h4>Механизм привязки переменных</h4>
<p>Чтобы избежать утечек памяти, следует более детально разобраться с процессом привязки переменных.</p>
<p>Локальные переменные, объявленные в начале процедуры, функции или метода (в дальнейшем "подпрограммы") обычно живут, пока подпрограмма активна. Анонимные методы продлевают жизнь этих переменных. </p>
<p>Если анонимный метод в своем теле обращается к внешней локальной переменной, эта перменная "захватывается". Захват означает продление жизни переменной, то есть она сохраняется до тех пор, пока хранится значение анонимного метода и не погибает с подпрограммой, в которой она объявлена. Следует учесть, что захватываются переменные, но не их значения. Если значение переменной изменяется после захвата при создании экземпляра анонимного метода, значение переменной, которую захватил анонимный метод также меняется, поскольку эти переменные хранятся в одном и том же месте и являются одной переменной. Захваченные переменные хранятся в куче, а не в стеке.</p>
<p>Значения анонимного метода являются значениями типа ссылка на метод и управляются подсчетом ссылок. Когда последняя ссыкла на данный анонимный метод выходит из области видимости или инициализируется значением nil или финализируется, переменные захваченные методом, выходят из области видимости.</p>
<p>Ситуация усложняется в случае, когда несколько методов захватывают одну переменную. Чтобы понять, как это работает во всех ситуациях, необходимо уточнить механику реализации.</p>
<p>Когда переменная захватывается, она добавляется в "рамочный объект", связанный с объявившей его подпрограммой. Каждый анонимный метод, объявленный в подпрограмме, преобразовывается в метод рамочного объекта, связанного с содержащей его подпрограммой. Наконец, каждый рамочный объект, созданный при создании анонимного метода или захвате переменной, прицепляется к своей родительской рамке дополнительной ссылкой (в том случае, если такая рамка существует и есть необходимость доступа к захваченной внешней переменной). Эти связи рамочных объектов со своими родительскими объектами также управляются подсчетом ссылок. Анонимный метод, объявленный во встроенной локальной подпрограмме и захватывающий переменные в своей родительской подпрограмме, сохраняет этот рамочный объект живым до тех пор, пока сам не выйдет из области видимости.</p>
Для примера рассмотрим ситуацию:
<pre class="brush:pas">type
TProc = reference to procedure;
procedure Call(proc: TProc);
// ...
procedure Use(x: Integer);
// ...
procedure L1; // рамка F1
var
v1: Integer;
procedure L2; // рамка F1_1
begin
Call(procedure // рамка F1_1_1
begin
Use(v1);
end);
end;
begin
Call(procedure // рамка F1_2
var
v2: Integer;
begin
Use(v1);
Call(procedure // рамка F1_2_1
begin
Use(v2);
end);
end);
end;
</pre>
<p>Чтобы определить, какой рамочный объект с чем связан, каждая подпрограмма и анонимный метод снабжается идентификатором рамки:</p>
<ul>
<li>v1 это переменная в F1;</li>
<li>v2 это переменная в F1_2 (захватывается F1_2_1);</li>
<li>Анонимный метод для F1_1_1 – это метод в F1_1;</li>
<li>F1_1 свзявается с F1 (F1_1_1 использует v1);</li>
<li>Анонимный метод для F1_2 – это метод в F1;</li>
<li>Анонимный метод для F1_2_1 – это метод в F1_2;</li>
</ul>
<p>Рамки F1_2_1 и F1_1_1 не нуждаются в рамочных объектах поскольку они не объявляют анонимных методов и не имеют захваченных переменных. Они не находятся на линии родства между встроенным анонимным методом и внешней захваченной переменной (у них есть явные рамки, которые хранятся в стеке).</p>
<p>При получении ссылки на анонимный метод F1_2_1 переменные v1 и v2 уже не уничтожаются. Если же, вместо этого, единственной оставшейся ссылкой после включения F1 будет F1_1_1, то не уничтожится только переменная v1.</p>
<p>В цепочках связей между ссылками на метод и рамками возможно создать цикл, который приведет к утечке памяти. Например, сохраняя анонимный метод напрямую (или не напрямую) в переменной, которую захватывает этот анонимный метод, можно создать цикл приводящий к утечке памяти.</p>
<br>
<h3>Применение анонимных методов</h3>
<p>Анонимные методы предлагают более чем простые указатели на все, что можно вызвать. Они предоставляют следующие преимущества:</p>
<ul>
<li>Привязка значений переменных;</li>
<li>Простой способ определения и применения методов;</li>
<li>Простой способ параметризовать используемый код.</li>
</ul>
<br>
<h4>Привязка переменных</h4>
<p>Анонимные методы предоставляют блок кода вместе с привязками переменных к окружению, в котором они определены, даже тогда, когда это окружение находится вне области видимости. Указатель на функцию или процедуру такого не может.</p>
<p>Например, инструкция adder := MakeAdder(20); из примера, приведенного выше производит переменную adder, которая захватывает привязку переменной к значению 20.</p>
<p>Некоторые другие языки, которые реализуют такие конструкции, обращаются к ним как к замыканиям. Исторически идея была в том, чтобы вычисление выражения вида adder := MakeAdder(20) создавало замыкание, представляющее собой объект, содержащий ссылки на привязки ко всем переменным, к которым происходит обращение в функции, а также к переменным, определенным снаружи этой функции. Что и замыкает его, захватывая значения переменных.</p>
<br>
<h4>Простота в использовании</h4>
<p>Следующий пример показывает типичное объявление класса и нескольких простых методов с последующим их вызовом:</p>
<pre class="brush:pas">type
TMethodPointer = procedure of object; // delegate void TMethodPointer();
TStringToInt = function(x: string): Integer of object;
TObj = class
procedure HelloWorld;
function GetLength(x: string): Integer;
end;
procedure TObj.HelloWorld;
begin
Writeln('Hello World');
end;
function TObj.GetLength(x: string): Integer;
begin
Result := Length(x);
end;
var
x: TMethodPointer;
y: TStringToInt;
obj: TObj;
begin
obj := TObj.Create;
x := obj.HelloWorld;
x;
y := obj.GetLength;
Writeln(y('foo'));
end.
</pre>
<p>Эти же методы вызываются с использованием анонимных методов:</p>
<pre class="brush:pas">type
TSimpleProcedure = reference to procedure;
TSimpleFunction = reference to function(x: string): Integer;
var
x1: TSimpleProcedure;
y1: TSimpleFunction;
begin
x1 := procedure
begin
Writeln('Hello World');
end;
x1; //вызов только что объявленного анонимного метода
y1 := function(x: string): Integer
begin
Result := Length(x);
end;
Writeln(y1('bar'));
end.
</pre>
<p>Обратите внимание, насколько проще и короче код, который использует анонимные методы. Он идеален в том случае, если вы хотите явно и без сложностей объявить эти методы и использовать их немедленно, без затрат и усилий на создание класса, который, возможно, никогда не будет больше использован. Результирующий код проще для понимания.</p>
<br>
<h4>Применение кода для передачи в качестве параметра</h4>
<p>Анонимные методы упрощают написание функций и структур, управляемых передаваемым в качестве параметра кодом, а не просто значениями. </p>
<p>Многопоточность – это хорошее применение для анонимных методов. Если вы хотите выполнять параллельно какой-то код, возможно вам пригодится функция на подобие этой:</p>
<pre class="brush:pas">type
TProcOfInteger = reference to procedure(x: Integer);
procedure ParallelFor(start, finish: Integer; proc: TProcOfInteger);</pre>
<p>Процедура ParallelFor работает в разных потоках. Предположим, что эта процедура правильно реализована, эффективно использует потоки в потоковом пуле и может быть легко использована для получения преимущества при работе с несколькими процессорами:</p>
<pre class="brush:pas">procedure CalculateExpensiveThings;
var
results: array of Integer;
begin
SetLength(results, 100);
ParallelFor(Low(results), High(results),
procedure(i: Integer) // \
begin // \ блок кода
results[i] := ExpensiveCalculation(i);// / используется
end // / как параметр
);
// пользуемся результатами
end;</pre>
<p>Как это могло быть реализовано без анонимных методов? Возможно при помощи класса-задачи с виртуальным абстрактным методом, для которого будет создан конкретный, не слишком естественный и интегрированный наследник, реализующий ExpensiveCalculation, и добавляющий все задания в очередь.</p>
<p>Здесь приведен "распараллеливаемый" алгоритм-абстракция, которая параметризуется кодом. В прошлом обычным решением для реализации такого паттерна было бы создание виртуального базового класса с одним или несколькими абстрактными методами (вспомним класс TThread и его абстрактный метод Execute). Как бы то ни было, анонимные методы позволяют сделать этот паттерн параметризуемым алгоритмами и упростить структуры данных, использующие код.</p>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-67590834402607078302012-03-25T19:48:00.002+04:002012-03-29T17:24:47.534+04:00Примеры простых программ на Lazarus для Win32 (Часть 1). Резюме.<div align="justify">
<p>Вообще резюме я решил написать, чтобы сформулировать отличия, которые я заметил в сравнении с Delphi. Lazarus производит вполне себе нормальное впечатление, пока делаешь несложные штуки. Местами я конечно поломал голову, но с ума не сошел ;-) (это я говорю скорее про вторую часть поста, которая на подходе).</p>
<p>Различия с Delphi 7 на самом деле небольшие (наверняка опять же, пока делаешь, что-то не слишком мудреное). Наверняка люди, берущиеся портировать под Lazarus, испытывают… приятное ;-)</p>
<a name='more'></a>
<p>В общем не так много чего я и заметил. Впредь буду внимательнее.</p>
<br>
<h4>Справочная система</h4>
<p>Имхо, справочные системы Lazarus и Delphi вполне себе сравнимы. Если хочется чего-то выяснить – можно попробовать на форум какой-нибудь слазить.</p><br>
<h4>Назначение обработчиков событий в RunTime. </h4>
<pre class="brush:pas">
button1.OnClick := @Button1Click;
</pre>
<p>В Delphi можно было бы обойтись и без оператора взятия адреса. </p><br>
<h4>Утечки памяти</h4>
<p>Отлов утечек памяти <a href="http://forum.lazarus.freepascal.org/index.php?topic=5240.0" target="_blank">предлагают</a> выполнять директивой компилятора –gh. (Может быть установлен в Проект-Параметры проекта-Параметры компилятора-Компоновка-Использовать модуль Heaptrc (-gh)). </p><br>
<h4>Sleep и ProcessMessages</h4>
<p>В Delphi, если необходимо в процессе выполнения одного обработчика события дать возможность приложению обработать другое событие,
нужно было использовать процедуру <a href="http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/SysUtils_Sleep.html"
target="_blank">Sleep()</a>. То есть:</p>
<pre class="brush:pas">
procedure Form1.Button1Click(Sender:TObject);
begin
Stopped:=false;
while true do
begin
DoSomething;
Sleep(20);
Application.ProcessMessages;
if Stopped then
Break;
end;
end;
procedure Form1.Button2Click(Sender:TObject);
begin
Stopped:=true;
end;
</pre>
<p>В Lazarus достаточно <a href="http://lazarus-ccr.sourceforge.net/docs/lcl/forms/tapplication.processmessages.html"
target="_blank">Application.ProcessMessages</a>. По крайней мере, в однопоточных приложениях <a href="http://forum.lazarus.freepascal.org/index.php?topic=13947.0" target="_blank">рекомендуют</a> делать именно так. Я попробовал, такой код, как в примере, со Sleep и работать не будет. </p>
<p>В документации наткнулся на недлинный рассказ про <a href="http://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial/ru" target="_blank">многопоточность</a>. </p><br>
<h4>Set of Char</h4>
<p>Примечательно, что Set of Char = ['а'..'я'] вызывает ошибку компиляции. Почему? По-простому отвечено <a href="http://www.freepascal.ru/forum/viewtopic.php?t=3591&view=unread#unread." target="_blank">тут</a>
<p>А! И еще приятно, что for in do работает.</p>
<p>Далее привожу фрагмент консольного приложения:</p>
<pre class="brush:pas">
type
TSomeChars = set of char;
procedure TMyApplication.DoRun;
procedure WriteSet(const S: TSomeChars);
var
c: char;
begin
WriteLn;
for c in S do
Write(c + ' ');
end;
begin
WriteSet(['0'..'9']);
WriteSet(['1'..'0']);
WriteSet(['0', '1'..'9']);
//WriteSet([['а'..'я']); Ошибка компиляции: Ordinal expression expected
WriteSet(['a'..'z', 'A'..'Z']);
ReadLn;
Terminate;
end;
</pre>
<p>Листинг:</p>
<pre>
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
</pre>
</div>svghttp://www.blogger.com/profile/15659944778208622928noreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-35202125887179754652012-03-25T19:48:00.001+04:002012-03-25T19:59:16.868+04:00Примеры простых программ на Lazarus под Win32 (Часть 1)<div align="justify">
<p>Довольно долго не было повода написать что-нибудь про Lazarus. Программированием последнее время я не занимался, но тут, что называется, “приперло”. Надо было сделать быстро, просто и без заморочек. Соответственно, качать пробную XE2 не стал, а решил положиться на бесплатный инструмент.</p>
<p>Задачи были следующие: выполнить форматирование нескольких тысяч финских телефонных номеров, лежащих в dbf, и выкачать из удаленной БД MySQL некоторую информацию, выгрузив ее в формат CSV.</p>
<p>Обо всех этих затеях <a name='more'></a></p>
<p>На самом деле тема форматирования телефонных номеров, в общем, не то чтобы новая. Навскидку можно извлечь из гугловской выдачи например вот такие ссылки:</p>
<p><a href="http://habrahabr.ru/post/102352/"target="_blank">Форматирование телефонных номеров на PHP (Хабр)</a></p>
<p><a href="http://code.google.com/p/libphonenumber/"target="_blank">libphonenumber - Java, C++ and Javascript library</a></p>
<p>Последний пример взят с code.google.com, что само по себе уже говорит о том, что решение достойно того, чтобы с ним ознакомиться. Библиотека действительно форматирует номера, но делает это вроде бы по достаточно простому алгоритму: в телефонном номере выделяется код страны, и известный этой библиотеке дополнительный код (префикс оператора или код города). В оставшуюся часть номера изменений никаких не вносится. Это возможно правильно с точки зрения мирового стандарта форматирования, однако не совсем устраивает меня, поскольку читать такие телефонные номера не слишком удобно.</p>
<p>Теперь собственно к делу. А дело в том, что в Финляндии длина номера может составлять от пяти до девяти цифр. Код города может включать в себя одну или две цифры (а с учетом так называемого <a href="http://en.wikipedia.org/wiki/Trunk_prefix"target="_blank">trunk prefix</a>) – от одной до трех цифр. Дополнительно все усложняет тот факт, что телефонные номера в исходной dbf появились из публичного источника. То есть финны сами вводили свои номера, при том делали это без учета какого-то единого правила (например, в каких то номерах trunk prefix присутствует, в каких то – нет).
<p>Далее ссылки по теме: </p>
<p><a href="http://www.wtng.info/wtng-358-fi.html"target="_blank">Краткое описание формата телефонных номеров в Финляндии</a>
<p><a href="http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland"target="_blank">Статья в WikiPedia</a>
<p><a href="http://dialcode.org/Europe/Finland/"target="_blank">Короткий список телефонных кодов городов Финляндии</a>
<p><a href="http://www.ecallchina.com/country/Finland-area-code.aspx"target="_blank">Список кодов городов и префиксов операторов</a>
<p><a href="http://everything2.com/title/Finnish+telephone+area+codes"target="_blank">Список кодов городов и префиксов операторов 2</a>
<p><a href="http://www.statkod.ru/finland_code1.html"target="_blank">Наиболее полный список телефонных кодов городов на русскоязычном ресурсе</a>
<p><a href="http://www.eniro.fi.prod.eniro.net/suuntanumerot/"target="_blank">Наиболее полный список кодов городов на финском ресурсе</a>
<p><a href="http://www.ficora.fi/en/index/palvelut/palvelutaiheittain/numerointi.html"target="_blank">Страницы с сайта FICORA (Finnish Communication Regulatory Authority)</a>
<p>Префиксы разных телефонных операторов могут начинаться с цифр кода города. А значит, отличить префикс оператора от кода города можно только зная, к какому городу относится этот номер (и зная код этого города). В моей dbfке присутствует название города и даже название провинции, к которой относится этот город. И это уже кое-что, поскольку вот тут есть список кодов по провинциям. Только по ссылке присутствует карта со старыми провинциями Финляндии (они менялись, было дело), а у меня – новые. И они редко пересекаются. Плюс ко всему, один и тот же населенный пункт может иметь два кода (видимо, в одну половину города нужно звонить по одному коду, в другую – по другому). </p>
<p>Короче говоря, задача непростая, но в общем, решаемая. К слову сказать, целью этого поста не является нахождение способа форматирования номеров, а изучение возможностей Lazarus. Окончательный вариант своей программы я приводить не стану, а выложу только основные моменты, чтобы было понятно, какими средствами и за счет чего было достигнуто приемлемое для меня решение.</p>
<p>Итак, мы имеем dbf файл с такой структурой:</p>
phone: string<br>
city: string<br>
province:string<br>
<p>Собираем со всех источников коды городов в электронную таблицу (ну, например, OpenOffice Calc). В результате получится примерно следующее:</p>
<table class="dttbl" width="300">
<tr> <th class="dtth">Город</th><th class="dtth">Код</th></tr><tr>
<tr><td class="dttd">Aanekoski</td><td class="dttd">14</td></tr>
<tr><td class="dttd">Aanekoski</td><td class="dttd">14</td></tr>
<tr><td class="dttd">Aetsa</td><td class="dttd">3</td></tr>
<tr><td class="dttd">Ahtari</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Ahtari</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Akaa</td><td class="dttd">3</td></tr>
<tr><td class="dttd">Alaharma</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Alajarvi</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Alajarvi</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Alastaro</td><td class="dttd">2</td></tr>
<tr><td class="dttd">Alavieska</td><td class="dttd">8</td></tr>
<tr><td class="dttd">Alavieska</td><td class="dttd">8</td></tr>
<tr><td class="dttd">Alavus</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Alavus</td><td class="dttd">6</td></tr>
<tr><td class="dttd">Anjalankoski</td><td class="dttd">5</td></tr>
<tr><td class="dttd">Anttola</td><td class="dttd">15</td></tr>
<tr><td class="dttd">...</td><td class="dttd">...</td></tr>
</table>
<p>Города в списке дублируются, но в этом ничего страшного нет, поскольку повторяющиеся варианты мы сможем удалить потом автоматически. Сохраняем таблицу в формате CSV (разделители – точки с запятой – ;). </p>
<p>Создаем новое приложение Lazarus, сохраняем его и помещаем в папку с проектом наш файл CSV. Для его разбора мы воспользуемся экземпляром класса TStringList, для этого объявим переменную соответствующего типа, в обработчик события OnCreate основной формы проекта впишем создание экземпляра, а в обработчик события OnDestroy – его разрушение.</p>
<pre class="brush:pas">
var
Form1: TForm1;
CityCodes:TStringList;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
CityCodes:=TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(CityCodes);
end;
</pre>
<p>TStringList всем хорош и в Lazarus. Добавляем на форму кнопку и в обработчике OnClick устанавливаем значение разделителя в ‘;’ , запрещаем дублирование данных в списке, загружаем наш CSV и тут же выгружаем его в новый файл, который уже не содержит дубликатов.</p>
<pre class="brush:pas">
procedure TForm1.Button1Click(Sender: TObject);
begin
CityCodes.Delimiter:=';';
CityCodes.Duplicates:=dupIgnore;
CityCodes.Sorted:=true;
try
CityCodes.LoadFromFile('citycodes.csv');
CityCodes.SaveToFile('citycodes1.csv');//сохраняем результат
except on E:Exception do
ShowMessage(E.Message);
end;
end;
</pre>
<cite>Замечание. Будьте внимательны! Режим dupIgnore не включается при Sorted = false.</cite>
<p>Теперь надо догрузить двойные коды городов, которые фигурируют в <a href="http://www.eniro.fi.prod.eniro.net/suuntanumerot/"target="_blank">этом</a> списке (например, Hartola -
03/014). Для этого сложим такие коды в отдельный CSV, отключим режим dupIgnore у нашего списка и догрузим города с двойным кодом.
Необходимо заметить, что метод LoadFromFile перед загрузкой очищает свой список, то есть, выполнив код</p>
<pre class="brush:pas">
CityCodes.LoadFromFile('citycodes1.csv');
CityCodes.LoadFromFile('citycodes2.csv');
CityCodes.SaveToFile('citycodes3.csv');
</pre>
<p>мы в получим в файле 'citycodes3.csv' только содержимое файла 'citycodes2.csv'.
Таким образом, для добавления в список новых кодов из файла можно завести дополнительную процедуру:</p>
<pre class="brush:pas">
procedure AddFromFile(TSL: TStringList; FN: string);
var
TS: TStringList;
begin
TS := TStringList.Create;
try
TS.LoadFromFile(FN);
except on E:Exception do
ShowMessage(E.Message);
end;
TSL.AddStrings(TS);
FreeAndNil(TS);
end;
</pre>
<p>Теперь поменяем код Button1Click следующим образом:</p>
<pre class="brush:pas">
procedure TForm1.Button1Click(Sender: TObject);
begin
CityCodes.Delimiter:=';';
//CityCodes.Duplicates:=dupIgnore; дубликаты теперь не отбрасываются
CityCodes.Sorted:=true;
try
//загружаем основные коды из полученного на предыдущем шаге списка
CityCodes.LoadFromFile('citycodes1.csv');
AddFromFile(CityCodes,'citycodes2.csv');//загружаем дополнительные коды
CityCodes.SaveToFile('citycodes3.csv'); //сохраняем результат
except on E:Exception do
ShowMessage(E.Message);
end;
end;
</pre>
<p>На самом деле, конечно, дополнительные коды можно добавить и при помощи Calca (или другого редактора CSV). Просто в контексте текущей задачи подгружать дополнительные коды мне приходилось неоднократно и иногда в довольно значительных объемах. Короче говоря, я решил, что загрузка таким способом облегчит мне жизнь и, возможно, не ошибся. </p>
<p>Теперь удалим ноли в начале кодов (trunk prefix). Код того же самого обработчика поменяем так:</p>
<pre class="brush:pas">
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
CityCodes.Delimiter := ';';
CityCodes.NameValueSeparator := ';';
CityCodes.Sorted := False;
try
CityCodes.LoadFromFile('citycodes3.csv');
for i := 0 to CityCodes.Count - 1 do
begin
S := CityCodes.ValueFromIndex[i];
if Pos('0', S) = 1 then
begin
Delete(S,1,1);
CityCodes.ValueFromIndex[i] := S;
end;
end;
CityCodes.SaveToFile('citycodes4.csv');
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
</pre>
<p>Здесь видимо следует дать пояснения по коду. Начнем с того, что опять же, решать такую задачу можно кучей всяких инструментов. В том числе Calc, MisroSoft Access и так далее и тому подобное. Но делал я это несколько раз, и таскать данные туда-сюда ленился. Кроме того, пример этот можно интересно прокомментировать, по сему – он тут.</p>
<p>Начнем с того, что <a href="http://lazarus-ccr.sourceforge.net/docs/rtl/classes/tstrings.namevalueseparator.html"target="_blank">NameValueSeparator</a> автоматически разделяет строки в списки на пары Name-Value. То есть до сих пор мы могли обратиться к элементу списка, например, так:</p>
<pre class="brush:pas">CityCodes[0]:='';</pre>
<p>таким образом мы могли “обнулить” строку нашего CSV файла. Могли получить получить весь текст из файла целиком. Например, таким образом:</p>
<pre class="brush:pas">Label1.Caption:=CityCodes.DelimitedText;</pre>
<p>Но для того чтобы добраться до конкретного кода города, нам пришлось бы “вручную” разбирать каждый элемент CityCodes. После выполнения инструкции</p>
<pre class="brush:pas">CityCodes.NameValueSeparator := ';';</pre>
<p>Весь наш список автоматически разбился на два столбца (поскольку названия городов и коды у нас как раз разделялись точками с запятой). И теперь мы можем искать код по названию города, например вот так:</p>
<pre class="brush:pas">S:=CityCodes.Values['Helsinki'];</pre>
<p>Правда, необходимо заметить, что в дальнейшем этот способ нам не слишком поможет, поскольку названия городов в списке у нас дублируются (из-за того, что мы добавили дополнительные коды).</p>
<p>Но получить код города из определенной строки мы можем:</p>
<pre class="brush:pas">S:=CityCodes.ValueFromIndex[0];</pre>
<p>И можем получить название города из определенной строки:</p>
<pre class="brush:pas">S:=CityCodes.Names[0];</pre>
<p>И последних двух возможностей нам будет вполне достаточно.</p>
<p>Далее нам необходимо сформировать список префиксов операторов связи. Делал я это совершенно аналогично составлению первого списка, за тем единственным исключением, что в принципе название оператора нам не слишком важно. Оно пригодится разве что только для того, чтобы иметь представление о том, какого рода префикс мы определили.</p>
<p>Коды во втором списке у нас уникальны, поэтому при создании списка нужно использовать инструкции </p>
<pre class="brush:pas">
OperCodes.Delimiter:=';';
OperCodes.Duplicates:=dupIgnore;
OperCodes.Sorted:=true;
</pre>
<p>В результате должно получиться два приблизительно таких списка (я их привожу неполностью):</p>
<table border="0";>
<tr><td valign="top" >
<table class="dttbl" >
<tr><th class="dtth">Код</th><th class="dtth" width="320">Оператор/доп. информация</th></tr>
<tr><td class="dttd">102</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">305</td><td class="dttd">TDC</td></tr>
<tr><td class="dttd">800</td><td class="dttd">PUBLIC/PREMIUM </td></tr>
<tr><td class="dttd">303</td><td class="dttd">TDC</td></tr>
<tr><td class="dttd">302</td><td class="dttd">TDC</td></tr>
<tr><td class="dttd">301</td><td class="dttd">TDC</td></tr>
<tr><td class="dttd">308</td><td class="dttd">Setera</td></tr>
<tr><td class="dttd">307</td><td class="dttd">Setera</td></tr>
<tr><td class="dttd">299</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">109</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">108</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">107</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">106</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">105</td><td class="dttd">Elisa</td></tr>
<tr><td class="dttd">600</td><td class="dttd">PUBLIC/PREMIUM</td></tr>
<tr><td class="dttd">700</td><td class="dttd">PUBLIC/PREMIUM</td></tr>
<tr><td class="dttd">...</td><td class="dttd">...</td></tr>
</table>
</td>
<td valign="top" padding=40px>
<table class="dttbl" width="300">
<tr> <th class="dtth">Город</th><th class="dtth">Код</th></tr><tr>
<td class="dttd">Espoo</td><td class="dttd">9</td></tr><tr>
<td class="dttd">Eura</td><td class="dttd">2</td></tr><tr>
<td class="dttd">Eurajoki</td><td class="dttd">2</td></tr><tr>
<td class="dttd">Evijarvi</td><td class="dttd">6</td></tr><tr>
<td class="dttd">Finstrom</td><td class="dttd">18</td></tr><tr>
<td class="dttd">Foglo</td><td class="dttd">18</td></tr><tr>
<td class="dttd">Forssa</td><td class="dttd">3</td></tr><tr>
<td class="dttd">Geta</td><td class="dttd">18</td></tr><tr>
<td class="dttd">Haapajarvi</td><td class="dttd">8</td></tr><tr>
<td class="dttd">Haapavesi</td><td class="dttd">8</td></tr><tr>
<td class="dttd">Hailuoto</td><td class="dttd">8</td></tr><tr>
<td class="dttd">Halikko</td><td class="dttd">2</td></tr><tr>
<td class="dttd">Halsua</td><td class="dttd">6</td></tr><tr>
<td class="dttd">Hameenkyro</td><td class="dttd">3</td></tr><tr>
<td class="dttd">Hameenlinna</td><td class="dttd">19</td></tr><tr>
<td class="dttd">Hameenlinna</td><td class="dttd">3</td></tr>
<tr><td class="dttd">...</td><td class="dttd">...</td></tr>
</table>
</td>
</tr>
</table>
<p>Теперь, чтобы не утруждать читателя инструкциями по настройке интерфейса, приведу просто код его динамической настройки (Кнопку, которую я создал ранее, я удалил вместе с обработчиком события OnClick).</p>
<p>В объявление Form1 добавились два обработчика событий:</p>
<pre class="brush:pas">
type
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure bStartClick(Sender: TObject);
procedure bStopClick(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
</pre>
<p>В секции implementation изменения следующие (все лишнее я опять убрал):</p>
<pre class="brush:pas">
var
CityCodes,OperCodes: TStringList;
Panel1: TPanel;
bStart, bStop: TButton;
mmOut: TMemo;
Dbf1: TDbf;
Aborted:boolean;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
CityCodes := TStringList.Create;
CityCodes.NameValueSeparator:=';';
OperCodes := TStringList.Create;
OperCodes.NameValueSeparator:=';';
Panel1 := TPanel.Create(Form1);
Panel1.Parent := Form1;
Panel1.Align := alTop;
Panel1.Height := 50;
bStart := TButton.Create(Panel1);
bStart.Parent := Panel1;
bStart.Caption := 'Start';
bStart.OnClick := @bStartClick;
bStop := TButton.Create(Panel1);
bStop.Parent := Panel1;
bStop.Caption := 'Stop';
bStop.OnClick := @bStopClick;
mmOut := TMemo.Create(Form1);
mmOut.Parent := Form1;
mmOut.Align := alClient;
mmOut.ScrollBars := ssAutoVertical;
Dbf1 := TDbf.Create(Form1);
Dbf1.FilePath := ExtractFilePath(Application.ExeName);
Dbf1.TableName := 'comp.dbf';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(CityCodes);
FreeAndNil(OperCodes);
end;
procedure TForm1.FormResize(Sender: TObject);
begin
bStart.Top := 10;
bStop.Top := bStart.Top;
bStart.Left := Form1.Width - bStart.Width - 10;
bStop.Left := bStart.Left - bStop.Width - 10;
end;
procedure TForm1.bStartClick(Sender: TObject);
begin
Aborted:=false;
Try
CityCodes.LoadFromFile('citycodes4.csv');
OperCodes.LoadFromFile('opercodes.csv');
Dbf1.Active := True;
mmOut.Lines.Add('Connected to dbf ' + Dbf1.TableName);
except
on E: Exception do
mmOut.Lines.Add(E.Message);
end;
end;
procedure TForm1.bStopClick(Sender: TObject);
begin
Aborted:=true;
end;
</pre>
<p>Ну вот. Все готово для обработки dbf. Результат трудов будем выкладывать в result.csv.
Добавляем проход по dbf и пустую функцию форматирования номеров:</p>
<pre class="brush:pas">
function FormatPhone(Ph, City, Province: string; var Dcdd:integer): string;
begin
Result := '';
end;
procedure TForm1.bStartClick(Sender: TObject);
var
Province, City, Phone, PhFormatted: string;
Total, Decoded: integer;
RSL: TStringList;
begin
Aborted := False;
Total := 0;
Decoded := 0;
mmOut.Clear;
RSL := TStringList.Create;
RSL.Add('phone_source;province;city;phone_formatted;');
try
CityCodes.LoadFromFile('citycodes4.csv');
OperCodes.LoadFromFile('opercodes.csv');
Dbf1.Active := True;
mmOut.Lines.Add('Connected to dbf ' + Dbf1.TableName);
while not dbf1.EOF do
begin
City := dbf1.FieldByName('city').AsString;
Province := dbf1.FieldByName('province').AsString;
Phone := dbf1.FieldByName('ph2').AsString;
PhFormatted := FormatPhone(Phone, City, Province, Decoded);
RSL.Add(Phone + ';' + Province + ';' + City + ';' + PhFormatted + ';');
Inc(Total);
if (Total mod 50 = 0) then
mmOut.Lines.Add(
IntToStr(Decoded) + ' / ' + IntToStr(Total) + ' NOT DECODED ' +
IntToStr(Total - Decoded));
Application.ProcessMessages;
if Aborted then
Break;
dbf1.Next;
end;
RSL.SaveToFile('result.csv');
except
on E: Exception do
mmOut.Lines.Add(E.Message);
end;
FreeAndNil(RSL);
mmOut.Lines.Add('JOB COMPLETED -----------------------');
mmOut.Lines.Add('Total: ' + IntToStr(Total) + ' Decoded: ' + IntToStr(Decoded));
dbf1.Active := False;
end;
</pre>
<p>В принципе здесь все просто: подготавливаем переменные, RSL (TStringList, в который сложим результаты), затем открываем dbf, проходим ее, попутно сохраняя значения полей в переменные, форматируем телефонные номера и записываем все в RSL, затем все закрываем, записываем RSL в файл, освобождаем память и выводим сообщение о завершении работы.</p>
<p>Наверное заслуживает отдельного внимание возможность останова процесса обработки. В обработчике bStopClick мы устанавливаем значение флага Aborted в true и после обработки каждой записи в bStartClick проверяем значение этого флага. В случае, если Aborted=false – выходим из цикла и планово завершаем процесс. Примечательно, что эта конструкция не будет работать без инструкции Application.ProcessMessages, которая позволяет обработать нажатие пользователем кнопки bStop в процессе работы цикла.</p>
<p>Теперь возьмемся за функцию FormatPhone. Для начала приведем телефоны "к единому знаменателю" (как я уже говорил, вводили их без соблюдения каких-либо правил).</p>
<p>Потом разобьем номер на части: </p>
<p>"конец номера" – последние четыре цифры, которые мы не разбираем, а просто вставляем в середину пробел и больше не трогаем;</p>
<p>"начало номера" – в нем содержится некоторый код или префикс, который необходимо выделить, после чего начальная номера будет представлять собой код/префикс+"хвост".</p>
<p>Функция DecodePrefix пока пуста и оставлена напоследок.</p>
<pre class="brush:pas">
function DecodePrefix(const Prefix, City, Province: string;
var Tail): string;
begin
Result := '';
end;
procedure SplitTail(const Splitter: string; var aTail: string);
begin
if Length(aTail) > 4 then
Insert(Splitter, aTail, 4);
end;
function FormatPhone(Ph, City, Province: string; var Dcdd:integer): string;
var
Ph2Decode,
PhoneStart, PhStart, PhoneEnd,
PTail: string;
i: integer;
begin
Result := '';
if Length(Ph) = 0 then
Exit;
Ph2Decode := Ph;
//убираем ноли (trunck prefix) из начала номера
if Ph2Decode[1] = '0' then
begin
Ph2Decode := Copy(Ph2Decode, 2, Length(Ph2Decode) - 1);
if Ph2Decode[1] = '0' then
Ph2Decode := Copy(Ph2Decode, 2, Length(Ph2Decode) - 1);
end;
//удаляем посторонние символы из номера (добавочных номеров
//нашем списке нет
for i := 1 to Length(Ph2Decode) do
if not (Ph2Decode[i] in ['0', '1'..'9']) then
Delete(Ph2Decode, i, 1);
//разделяем номер на "начало" и "конец"
//в конецевую часть номера сразу вставляем разделитель
//и больше ее не трогаем
PhoneEnd := Copy(Ph2Decode, Length(Ph2Decode) - 3, 4);
Insert(' ', PhoneEnd, 3);
//на всякий случай делаем копию начала номера
PhoneStart := Copy(Ph2Decode, 1, Length(Ph2Decode) - 4);
PhStart := PhoneStart;
//разбираем начальную часть номера
//PTail - это "хвост", оставшийся от начальной части номера
//после выделения префикса
PhoneStart := DecodePrefix(PhoneStart, City, Province, PTail);
if PhoneStart <> '' then
begin
//если удалось выделить код города или префикс
Inc(Dcdd);
if PTail = '' then
begin
Result := PhoneStart + ' ' + PhoneEnd;
end
else
begin
SplitTail(' ', PTail);
Result := PhoneStart + ' ' + PTail + ' ' + PhoneEnd;
end;
end
else
begin
//если не удалось выделить код города или префикс
//просто разделяем "начало" номера
SplitTail(' ', PhStart);
Result := PhStart + ' ' + PhoneEnd;
end;
Result := '+358 ' + Result;
end;
</pre>
<p>Ну и собственно функция DecodePrefix. Оговорюсь, что результаты работы приведенной здесь функции меня лично не слишком впечатляют. Но она худо-бедно разобрала приблизительно 90% номеров. Улучшить ее можно за счет более "тонкой" настройки, которую я не привожу, поскольку задачу она все равно не решает на 100% (может быть у меня с исходными данными что-то не так?;-), места занимает сравнительно много, и объяснять, что она делает, придется долго. Я поленился. Помните, в начале поста я сказал, что пример этот приводится для изучения возможностей Lazarus? Вот.</p>
<pre class="brush:pas">
function DecodePrefix(const Prefix, City, Province: string;
var Tail: string): string;
var
i, j: integer;
Oper, Pref1, Pref2, Place, Code: string;
begin
//ищем совпадения кодов городов
Tail := '';
Pref2 := Prefix;
for j := 2 downto 1 do
begin
//устанавливаем длину префикса и "хвоста"
//например, если Prefix='235' то при первом проходе
//будем искать код '23', Tail = '5'
// при втором проходе будем искать код 2, Tail='35'
Pref1 := Copy(Prefix, 1, j);
Tail := Copy(Prefix, j + 1, Length(Prefix) - j);
for i := 0 to CityCodes.Count - 1 do
begin
Place := CityCodes.Names[i];
Code := CityCodes.ValueFromIndex[i];
//считаем, что код найден, только если город или провинция совпадают
if (Code = Pref1) and ((Place = City) or (Place = Province)) then
begin
Result := Code;
Exit;
end;
end;
end;
//ищем совпадения с префиксами операторов
Pref2 := Prefix;
Tail := '';
repeat
//здесь префикс и "хвост" настраиваются аналогично
//тому, как это делалось с кодами городов.
//только в качестве счетчика используется длина Pref2
//от которого на каждой итерации отделяется одна цифра
Pref1 := Copy(Pref2, 1, Length(Pref2) - 1);
Tail := Copy(Pref2, Length(Pref2), 1) + Tail;
Oper := OperCodes.Values[Pref1];
//если в списке найден оператор с таким префиксом,
//значит можно возвращать результат
if Oper <> '' then
begin
Result := Pref1;
Exit;
end;
Pref2 := Copy(Pref2, 1, Length(Pref2) - 1);
until Length(Pref2) <= 2;
end;
</pre>
<p>Вот, собственно, и все. Резюме я выкладываю отдельной <a href="http://pascal-study.blogspot.com/2012/03/lazarus-win32-1_25.html#more">запиской</a>, а то этот пост получился длинноват. </p>
<p>Комментарии приветствуются. Не возьмусь назвать свои примеры и решения образцовыми, так что Welcome! </p>
<p>Весь проект можно скачать <a href="http://dl.dropbox.com/u/65057001/parser4blog.rar">отсюда</a>. Dbf, извините, не выложу. Как-никак, персональные данные – хоть и финские;-)</p>
<br>
<p>PS. Про TStringList написано очень основательно вот <a href="http://forum.vingrad.ru/topic-208756.html"target="_blank">тут.</a></p>
</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-1920033394599582564.post-39004387063190043232012-03-01T12:49:00.000+04:002012-03-01T12:49:16.701+04:00Поля<cite>Перевод из справочной системы Delphi</cite><br/>
<div align = "justify">
<br>
<h3>О полях</h3>
<p>Поле схоже с переменной, принадлежащей объекту. Поля могут быть любого типа, включая классы (то есть поля могут хранить ссылки на объекты). Поля обычно имеют видимость private.</p>
<a name='more'></a><br/>
<p>Чтобы определить компонент-поле для класса, просто объявите поле, как вы стали бы объявлять переменную. Например, следующее объявление создает класс с именем TNumber, единственным компонентом которого, кроме тех, что наследуются от System.TObject, является целочисленное поле с именем Int:</p>
<pre class="brush:pas"> type
TNumber = class
var
Int: Integer;
end;</pre>
<p>Ключевое слово var является опциональным, однако, в том случае, если оно опущено, все объявления полей должны предшествовать объявлению свойств или методов. После объявления свойства или метода при помощи ключевого слова var можно объявить дополнительные поля.</p>
<p>Поля связываются статически, то есть ссылки на них разрешаются во время компиляции. Чтобы понять, что это означает, рассмотрим следующий пример:</p>
<pre class="brush:pas">type
TAncestor = class
Value: Integer;
end;
TDescendant = class(TAncestor)
Value: string; // hides the inherited Value field
end;
var
MyObject: TAncestor;
begin
MyObject := TDescendant.Create;
MyObject.Value := 'Hello!' // error
(MyObject as TDescendant).Value := 'Hello!' // works!
end;</pre>
<p>Хотя MyObject хранит экземпляр TDescendant, он объявлен как TAncestor. По этому компилятор интерпретирует MyObject.Value как ссылку на целочисленное поле, объявленное в TAncestor. Тем не менее, оба поля существуют в объекте TDescendant, но наследуемое поле Value перекрыто новым и доступно только через преобразование типа.</p>
<p>Константы и типизированные константы могут встречаться в классах и неанонимных записях в глобальной области видимости. И константы и типизированные константы можно встретить также внутри объявления встроенных типов. Константы и типизированные константы могут встретиться только внутри объявления класса, причем если он объявлен локально по отношению к процедуре (то есть не может встретиться внутри записей, объявленных внутри процедуры).</p>
<br>
<h3>Поля класса</h3>
<p>Поля класса - это поля данных, объявленные в классе и доступные без ссылки на объект (в отличии от обычных "полей экземпляра", которые обсуждались выше). Данные, хранимые в поле класса, хранятся во всех экземплярах класса и могут быть доступны при обращении к классу или переменной, представляющей экземпляр класса.</p>
<p>Вы можете включить блок полей класса в объявление класса при помощи объявления блока class var. Все поля, объявленные после ключевых слов class var имеют атрибуты статического хранения. Блок class var завершается объявлением:</p>
<ul>
<li>Другим объявлением class var или var;</li>
<li>процедуры или функции (то есть метода) (включая процедуры и функции класса);</li>
<li>свойства (включая свойства класса);</li>
<li>деструктора или конструктора; </li>
<li>Может завершаться спецификатором области видимости (public, private, protected, published, strict private и strict protected).</li>
</ul>
Например:
<pre class="brush:pas">type
TMyClass = class
public
class var // Introduce a block of class static fields.
Red: Integer;
Green: Integer;
Blue: Integer;
var // Ends the class var block.
InstanceField: Integer;
end;</pre>
<p>Доступ к полям класса Red, Green и Blue возможен следующим образом:</p>
<pre class="brush:pas">
TMyClass.Red := 1;
TMyClass.Green := 2;
TMyClass.Blue := 3;</pre>
<p>Поля класса могут быть доступны через экземпляр класса. Выполнив объявление:</p>
<pre class="brush:pas">
var
myObject: TMyClass;</pre>
<p>Можно получить результат, аналогичный приведенному выше присваиванию значений переменным Red, Green и Blue:</p>
<pre class="brush:pas">
myObject.Red := 1;
myObject.Green := 2;
myObject.Blue := 3;
</pre>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-1920033394599582564.post-73325038439305986572012-03-01T08:53:00.000+04:002012-03-01T08:53:04.107+04:00Классы и объекты<cite>Перевод из справочной системы Delphi</cite><br/>
<div align = "justify"><br><h3>Тип класс</h3>
<p>Класс или тип класса определяет структуру, состоящую из полей, методов и свойств. Экземпляры классов называются объектами. Поля, методы и свойства класса называются его компонентами или членами.</p>
<ul>
<li>Поле представляет собой переменную, которая является частью объекта. Как и поля записей, поля классов представляют элементы данных, которые присутствуют в каждом экземпляре класса.</li>
<li>Метод – это процедура или функция, связанная с классом. Большая часть методов оперирует объектами (т.е. экземплярами классов). Некоторые методы (называемые методами класса) работают с классами как с типами.</li>
<li>Свойство – это интерфейс к данным, связанным с объектом (которые часто хранятся в полях). Свойства имеют спецификаторы доступа, которые определяют, каким образом данные могут быть прочитаны или изменены. Из других частей программы, то есть снаружи объекта, свойство в большинстве случаев выглядит как поле.</li>
</ul><a name='more'></a><br/>
<p>Объекты – это динамически выделяемые блоки памяти, структура которых определяется их типом. Каждый объект имеет уникальную копию полей, определенных в описании класса, но все экземпляры классов используют одинаковые методы. Объекты создаются и разрушаются при помощи специальных методов, называемых конструкторами и деструкторами.</p>
<p>Переменная типа класс на самом деле является указателем, который ссылается на объект. Следовательно, несколько переменных может ссылаться на один объект. Как и прочие указатели, переменные типа класс могут принимать значение nil. Нет необходимости явным образом разыменовывать переменную типа класс для того, чтобы получить доступ к объекту, на который она ссылается. То есть:</p>
<pre class="brush:pas">SomeObject.Size := 100;
//устанавливает значение свойства объекта SomeObject</pre>
Нет необходимости записывать эту операцию как:
<pre class="brush:pas">SomeObject^.Size := 100;</pre>
<p>Класс должен быть объявлен прежде, чем будет создан его экземпляр (вы не можете объявить класс внутри объявления переменной). Объявлять класс следует только во внешней области видимости программы или модуля, но не внутри объявления процедуры или функции.
Объявление класса имеет вид:<pre class="brush:pas">type
className = class [abstract | sealed] (ancestorClass)
memberList
end;</pre>где className – любой допустимый идентификатор;
<p>sealed и abstract – опциональные зарезервированные слова; Здесь синтаксис [abstract | sealed] обозначает, что может быть использовано только одно из зарезервированных слов. При этом значимыми являются непосредственно ключевые слова (скобки и символ "|" не должны присутствовать в объявлении). </p>
<p>Если класс помечен ключевым словом sealed, наследование от этого класса запрещается.</p>
<p>Целый класс может быть объявлен абстрактным даже в том случае, если он не содержит в своем описании абстрактных виртуальных методов.</p>
<cite>Замечание: Для обратной совместимости Delphi позволяет создавать экземпляры класса, объявленного как abstract, но пользоваться такой возможностью не следует.</cite>
<p>Класс не может быть одновременно объявлен как abstract и sealed.</p>
<p>(ancestorClass) – Указание на класс-предок может не указываться; Если пропущено указание на класс-предок (ancestorClass), новый класс наследуется непосредственно от класса System.TObject. Если в объявление класса включается указание на класс-предок и список компонентов класса пуст, в объявлении можно не указывать ключевого слова end. Объявление класса может так же включать список интерфейсов, реализуемых классом.</p>
<p>memberList - объвляет компоненты класса (поля, свойства и методы);</p>
<p>Методы описываются в объявлении класса в виде заголовков, без указания тел. Программный код методов должен быть указан в других частях программы.
Далее приведено объявление класса TMemoryStream из модуля Classes:</p>
<pre class="brush:pas">type
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: Longint;
procedure SetCapacity(NewCapacity: Longint);
protected
function Realloc(var NewCapacity: Longint): Pointer;
virtual;
property Capacity: Longint read FCapacity
write SetCapacity;
public
destructor Destroy; override;
procedure Clear;
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SetSize(NewSize: Longint); override;
function Write(const Buffer; Count: Longint): Longint;
override;
end;</pre><p>Класс Classes.TMemoryStream происходит от Classes.TCustomMemoryStream, наследуя его методы. При этом он переопределяет несколько методов и свойств, включая деструктор. Конструктор Create наследуется без изменений от класса System.TObject (по этому он не указывается в объявлении). Каждый компонент класса, объявляется как private, protected, public (в объявлении нет методов, объявленных как published). Значение этих ключевых слов объясняется ниже.</p>
После объявления класса можно создать его экземпляр:
<pre class="brush:pas">var stream: TMemoryStream;
stream := TMemoryStream.Create;
</pre><br>
<h3>Наследование и область видимости</h3>
<p>При объявлении класса вы можете указать его непосредственного предка. Например, объявление:</p>
<pre class="brush:pas">type TSomeControl = class(TControl);</pre>
<p>объявляет класс с именем TSomeControl, наследуемый от Vcl.Controls.TControl. Класс автоматически наследует все компоненты класса-непосредственного предка. Каждый класс может объявить новые компоненты и переопределить наследуемые, но не может удалять компоненты, определенные в классе-предке. Следовательно TSomeControl содержит все компоненты, определенных в Vcl.Controls.TControl и его предках.</p>
<p>Область видимости идентификатора компонента начинается от его объявления, завершается концом объявления класса и расширяется за счет всех наследников класса и блоков кода методов, определенных в классе и всех его наследников.</p>
<br><h3>TObject и TClass</h3>
<p>Класс System.TObject, объявленный в модуле System, является обязательным предком всех прочих классов. System.TObject определяет лишь несколько методов, включая конструктор и деструктор. Вдобавок к System.TObject, модуль System объявляет ссылку на тип класса System.TClass:</p>
<pre class="brush:pas">TClass = class of TObject;</pre>
<p>Если при объявлении класса опускается предок, класс наследуется напрямую от System.TObject. То есть:</p>
<pre class="brush:pas">type TMyClass = class
...
end;</pre>
эквивалентно:
<pre class="brush:pas">type TMyClass = class(TObject)
...
end;</pre>
<p>Последняя форма является более предпочтительной, поскольку облегчает чтение кода.</p>
<br><h3>Совместимость типов классов</h3>
<p>Тип класса совместим по присваиванию с предками. Из этого следует, что переменная типа класс может ссылаться на экземпляр любого класса предка. Например, объявив:</p>
<pre class="brush:pas">type
TFigure = class(TObject);
TRectangle = class(TFigure);
TSquare = class(TRectangle);
var
Fig: TFigure;</pre>
<p>Переменной Fig можно присвоить значения типа TFigure, TRectangle или TSquare.</p>
<br>
<h3>Объектный тип</h3>
<p>Компилятор Delphi Win32 разрешает альтернативный сиснаксис объявления типа класса. Вы можете объявить объектный тип, используя следующий синтаксис:</p>
<pre class="brush:pas">type ObjectTypeName = object (AncestorObjectType)
memberList
end;</pre>
<p>где ObjectTypeName это любой допустимый идентификатор, указание предка (AncestorObjectType) является опциональным, а memberList объявляет поля, методы и свойства. Если (ancestorObjectType) опущен в объявлении – новый тип не имеет предка. Объектный тип не может иметь членов с видимостью published.</p>
<p>Если объектный тип не наследуется от System.TObject, он не предоставляет встроенных конструкторов, деструкторов или других методов. Вы можете создаваться экземпляры объктного типа при помощи процедуры New, разрушать их процедурой Dispose или просто объявлять переменные объектного типа, также, как это происходит с записями.</p>
<p>Объектные типы поддерживаются только для обратной совместимости. Их использование не рекомендовано на платформе Win32.</p><br>
<h3>Видимость компонентов класса</h3>
<p>Каждый компонент класса имеет атрибут, называемый видимостью, который определяется одним из зарезервированных слов private, protected, public, published или automated. Например:</p>
<pre class="brush:pas">
published property Color: TColor read GetColor
write SetColor;
</pre>
<p>объявляет свойство Color с областью видимости published. Видимость определяет, где и как будет доступен компонент класса. Наименьшая доступность у видимости private, protected имеет среднюю доступность, а видимость public, published и automated имеют самую большую доступность.</p>
<p>Если в объявлении компонента класса опускается спецификатор видимости, компонент имеет такую же видимость, как у компонента, объявленного перед ним. Компоненты без спецификаторов видимости, следующие в начале объявления класса, по умолчанию получают видимость published (если у компилятора установлен режим {$M+}, или если класс, от которого наследуется объявляемый класс, скомпилирован в режиме {$M+}) или public (во всех остальных случаях).</p>
<p>Для удобства чтения кода, наилучшим решением будет организовать объявление класса с учетом видимости его компонентов: в начало объявления помещать компоненты с видимостью private , затем – компоненты с видимостью protected и так далее. В этом случае зарезервированное слово для обозначения видимости указывается однократно в начале "секции". Таким образом, типичное объявление класса выглядит:</p>
<pre class="brush:pas">type
TMyClass = class(TControl)
private
{ private declarations here }
protected
{ protected declarations here }
public
{ public declarations here }
published
{ published declarations here }
end;</pre>
<p>Переопределяя класс-наследник, вы можете увеличить видимость его компонентов, но не снизить ее. Например, свойство, помеченное как protected в классе-предке, может быть объявлено как public в классе-наследнике, не может быть объявлено как private. Более того, компоненты с видимостью published не могут получить видимость public в классе-наследнике. </p><br>
<h4>Компоненты с видимостью private, protected и public</h4>
<p>Компоненты с видимостью private невидимы снаружи модуля или программы, в которой объявлен класс. Другими словами, метод с видимостью private не может быть вызван из другого модуля, а свойство с такой видимостью не может быть прочитано или изменено из другого модуля.</p>
<p>Размещая связанные объявления классов в одном и том же модуле, вы можете предоставить классам доступ к чужим компонентам с видимостью private, но при этом все таки ограничиваете доступ к ним.</p>
<p>Компоненты с видимостью protected доступны в модуле, где объявлен класс, а также в любом классе-наследнике (вне зависимости от того, в каком модуле он объявлен). Метод класса-предка с видимостью protected может быть вызван из объявления любого метода, принадлежащего классу-наследнику, и любое свойство (или поле) с видимостью protected может быть прочитано или изменено в методе класса-наследника. Компоненты, предназначенные для использования при реализации классов-наследников обычно имеют видимость protected.</p>
<p>Компоненты с видимостью public видимы везде, где доступен их класс.</p>
<br><h4>Спецификаторы строгой видимости</h4>
<p>В дополнение к спецификаторам private и protected, компилятор Delphi поддерживает настройки видимости с более строгими ограничениями. Это спецификаторы видимости strict private и strict protected. Эти настройки могут быть использованы в приложениях на платформе Win32.</p>
<p>Компоненты классов с видимостью strict private доступны только внутри класса, в котором они объявлены. Они не видны другим процедурам или функциям, объявленным внутри модуля, в котором объявлен этот класс. Компоненты класса с видимостью strict protected видимы внутри класса, в котором они объявлены и внутри всех классов-наследников, вне зависимости от того, где объявлены они. Более того, когда компоненты экземпляра класса (объявление без использования ключевых слов class или class var) объявлены strict private или strict protected, они недоступны снаружи экземпляра класса, в котором они объявлены. Экземпляр класса не может получить доступ к компонентам с видимостью strict protected или strict protected, относящимся к другому экземпляру этого же класса.</p>
<p>Традиционные в Delphi спецификаторы видимости private совпадают с видимостью assembly в CLR. Спецификатор видимости protected совпадает либо с видимостью Family or Assembly в CLR.</p>
<cite>Замечание: Ключевое слово strict обрабатывается как директива в контексте объявления класса. Внутри объявления класса вы не можете объявить метод с именем "strict", но это возможно вне объявления класса.</cite><br>
<br><h4>Компоненты с видимостью published</h4>
<p>Компоненты с видимостью published имеют такую же видимость, как и компоненты, помеченные как public. Различие между ними заключается в том, что информация о типах в режиме выполнения программы (run-time type information - RTTI) создается для компонентов с видимостью published. RTTI позволяет приложению динамически получать информацию о полях, свойствах и методах класса. RTTI используется для получения доступа к значениям свойств при загрузке и сохранении файлов, отображения свойств объекта в Инспекторе Объектов и связи специальных методов (называемых обработчиками событий) со свойствами-событиями.</p>
<p>Компоненты с видимостью Published имеют ограничения по типам данных. Видимость published могут иметь компоненты порядковых, строковых, интерфейсных, вариантных типов, типа класс и указатели на метод. Видимость published могут также иметь компоненты типа множество, для которых указываются верхняя и нижняя границы базового типа с порядковыми значениями от 0 до 31 (другими словами, множество должно помещаться в байт, слово или двойное слово). Кроме того, "опубликован" может быть любой вещественный тип (кроме Real48). Свойства типа массив (array type), в отличии от свойств-массивов (array properties), не могут иметь видимость published.</p>
<p>Некоторые свойства хоть и могут иметь видимость published, неполностью поддерживаются потоковой системой. Это относится к типам запись, свойствам-массивам любых "публикуемых" типов и свойствам перечисляемых типов, имеющих анонимные значения. Если вы устанавливаете видимость published для таких свойств, Инспектор Объектов не будет корректно отображать их, а значение свойства не будет сохраняться при сохранении объектов на диск.</p>
<p>Все методы публикуемы, но класс не может публиковать более одного перегружаемого метода одновременно. Поля могут иметь видимость published только в том случае, когда относятся к типу класс или интерфейсному типу.</p>
<p>Класс не может иметь компонентов с видимостью published за исключением тех случаев, когда он компилируется с ключом компилятора {$M+} или наследуется от класса, скомпилированного в режиме {$M+}. Большинство классов, имеющих компоненты с видимостью published, наследуются Classes.TPersistent, который скомпилирован с ключом {$M+}. Таким образом, необходимости в частом использовании директивы $M нет.</p>
<cite>Замечание: В секциях published классов запрещено размещение идентификаторов, содержащих символы Unicode. Кроме того, идентификаторы, содержащие символы Unicode запрещены при объявлении типов, которые используют члены с видимостью published.</cite><br>
<br><h4>Компоненты с видимостью automated (только платформа Win32)</h4>
<p>Компоненты с видимостью automated имеют ту же видимость, что и компоненты с видимостью public. Отличие заключается в том, что для компонентов с видимостью automated генерируется информация типа Automation (Automation type information), которая требуется для серверов Automation. Компоненты с видимостью automated обычно встречаются только в классах Win32. Зарезервированное слово automated поддерживается для обратной совместимости. Класс TAutoObject в модуле ComObj не использует automated.</p>
<p>Для методов и свойств, объявленных как automated действуют следующие ограничения:</p>
<ul><li>Типы всех свойств, параметры свойств-массивов, параметры методов и значения, возвращаемые функциями должны быть автоматизируемы. К автоматизируемым типам относятся: Byte, Currency, Real, Double,Longint, Integer, Single, Smallint, AnsiString, WideString, TDateTime, Variant, OleVariant, WordBool и все интерфейсные типы.</li>
<li>Объявления методов должны использовать конвенцию вызова по умолчанию register. Они могут быть виртуальными, но не динамическими.</li>
<li>Объявление свойств может включать спецификаторы доступа (read и write), но другие спецификаторы (index, stored, default и nodefault) запрещены. Спецификаторы доступа должны указывать на идентификаторы методов, использующих конвенцию вызова по умолчанию register. Идентификаторы полей в этом случае запрещены.</li>
<li>Объявления свойств должны указывать тип. Перекрытие свойств запрещено.</li></ul>
<p>Объявление метода или свойства с видимостью automated может включать директиву dispid. Включение в директиву dispid идентификатора, который уже использован, приводит к ошибке.</p>
<p>На платформе Win32 за этой директивой должна следовать целочисленная константа, которая определяет диспетчерский идентификатор Automation (Automation dispatch ID) для члена класса. В противном случае компилятор автоматически назначает компоненту диспетчерский идентификатор, который больше чем наибольший, встречающийся у методов и свойств в классе и его предках. </p>
<br><h3>Упреждающие объявления и взаимозависимые классы</h3>
<p>Если объявление класса заканчивается словом класс и точкой с запятой (;), то есть имеет вид:</p>
<pre class="brush:pas">type className = class;</pre>
<p>без указания предка или компонентов класса, перечисленных после слова class, объявление является упреждающим. Упреждающее объявление должно быть разрешено объявлением такого же класса в этой же секции объявления типов. Другими словами, между упреждающим объявлением и определяющим объявлением не должно быть ничего, кроме объявления других типов.</p>
<p>Упреждающие объявления позволяют создавать взаимозависимые классы. Например:</p>
<pre class="brush:pas">type
TFigure = class; // упреждающее объявление
TDrawing = class
Figure: TFigure;
// ...
end;
TFigure = class // определяющее объявление
Drawing: TDrawing;
// ...
end;</pre>
<p>Не стоит путать упреждающие объявления с полными объявлениями классов, наследуемых от System.TObject и не объявляющих дополнительных членов.</p>
<pre class="brush:pas">type
TFirstClass = class; // это упреждающее объявление
TSecondClass = class // это полное объявление класса
end; //
TThirdClass = class(TObject);
// это полное объявление класса
</pre>
</div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-1920033394599582564.post-13810304503756801452012-02-25T22:43:00.000+04:002012-02-25T23:02:11.578+04:00Объявляемые константы<cite>Перевод из справочной системы Delphi</cite><br/>
<div align = "justify">
<p>Несколько видов конструкций языка называют "константами". Существуют числовые константы (их также называют числами), строковые константы (их называют символьными строками или строковыми литералами), например, "Hello world!". Все перечисляемые типы определяют константы, которые являются значениями этих типов. Есть предопределенные константы, такие как True, False и nil. Наконец, есть константы, создаваемые объявлением.</p>
<p>Объявляемые константы могут быть либо "чистыми константами" или типизированными константами. Эти два вида констант в целом похожи, но работают по разным правилам и применяются для различных целей.</p>
<a name='more'></a><br/>
<h3>Чистые константы</h3>
<p>Чистая константа – это объявленный идентификатор, значение которого не может меняться. Например:</p>
<pre class="brush:pas">const MaxValue = 237;</pre>
<p>объявляет константу с именем MaxValue, которая возвращает целочисленное значение 237. Синтаксис объявления чистой константы:</p>
<pre class="brush:pas">const identifier = constantExpression</pre>
<p>где identifier – это любой допустимый идентификатор, а constantExpression – это выражение, которое компилятор может вычислить без выполнения программы.</p>
<p>Если constantExpression возвращает порядковое значение, вы можете определить тип объявленной константы при помощи преобразования типа. Например:</p>
<pre class="brush:pas">const MyNumber = Int64(17);</pre>
<p>объявляет константу с именем MyNumber и типом Int64, которая возвращает целое число 17. В остальных случаях тип объявленной константы – это тип constantExpression.</p>
<ul>
<li>Если constantExpression – это символьная строка, объявленная константа совместима любым строковым типом. Если символьная строка имеет длину 1, она также будет совместима с любым символьным типом.</li>
<li>Если constantExpression – это дробное число, его тип - Extended. А если целое число – ее тип можно определить по таблице, приведенной ниже.</li>
</ul>
<br>
Типы целочисленных констант
<table valign="top" cellspacing = "2" cellpadding="5" border="1">
<tr><td>Диапазон константы (шестнадцатеричный)</td><td>Диапазон константы (десятичный)</td><td>Тип</td><td>Псевдоним</td></tr>
<tr><td>0-$FF</td><td>0-255</td><td>Byte</td><td>UInt8</td></tr>
<tr><td>0-$FFFF</td><td>0-65535</td><td>Word</td><td>UInt16</td></tr>
<tr><td>0-$FFFFFFFF</td><td>0-4294967295</td><td>Cardinal</td><td>UInt32, LongWord</td></tr>
<tr><td>0-$FFFFFFFFFFFFFFFF</td><td>0-18446744073709551615</td><td>UInt64</td><td></td></tr>
<tr><td>-$80 - $7F</td><td>-128 - 127</td><td>ShortInt</td><td>Int8</td></tr>
<tr><td>-$8000 - $7FFF</td><td>-32768 - 32767</td><td>SmallInt</td><td>Int16</td></tr>
<tr><td>-$80000000 - $7FFFFFFF</td><td>-2147483648 - 2147483647</td><td>Integer</td><td>Int32, LongInt</td></tr>
<tr><td>-$8000000000000000 - $7FFFFFFFFFFFFFFF</td><td>-9223372036854775808 - 9223372036854775807</td><td>Int64</td><td></td></tr>
</table>
<br>
32-битный стандартный целочисленный тип
<table valign="top" cellspacing = "2" cellpadding="5" border="1">
<tr><td>Диапазон константы (шестнадцатеричный)</td><td>Диапазон константы (десятичный)</td><td>Тип</td><td>Эквивалент</td></tr>
<tr><td> -$80000000 - $7FFFFFFF</td><td>-2147483648 - 2147483647</td><td>NativeInt</td><td>Integer</td></tr>
<tr><td>0 - $FFFFFFFF</td><td>0 - 4294967295</td><td>NativeUInt</td><td>Cardinal</td></tr>
</table>
<br>
64-битный стандартный целочисленный тип
<table valign="top" cellspacing = "2" cellpadding="5" border="1">
<tr><td>Диапазон константы (шестнадцатеричный)</td><td>Диапазон константы (десятичный)</td><td>Тип</td><td>Эквивалент</td></tr>
<tr><td>-$8000000000000000 - $7FFFFFFFFFFFFFFF</td><td>-9223372036854775808 - 9223372036854775807</td><td>NativeInt</td><td>Int64</td></tr>
<tr><td>0 - $FFFFFFFFFFFFFFFF</td><td>0 - 18446744073709551615</td><td>NativeUInt</td><td>UInt64</td></tr>
</table>
<br>
Далее приведены примеры объявления констант:
<pre class="brush:pas">
const
Min = 0;
Max = 100;
Center = (Max - Min) div 2;
Beta = Chr(225);
NumChars = Ord('Z') - Ord('A') + 1;
Message = 'Out of memory';
ErrStr = ' Error: ' + Message + '. ';
ErrPos = 80 - Length(ErrStr) div 2;
Ln10 = 2.302585092994045684;
Ln10R = 1 / Ln10;
Numeric = ['0'..'9'];
Alpha = ['A'..'Z', 'a'..'z'];
AlphaNum = Alpha + Numeric;</pre>
<br>
<h3>Константные выражения</h3>
<p>Константное выражение – это выражение, значение которого компилятор может определить без выполнения программы, в которую оно включено. Константные выражения включают числа, символьные строки, чистые константы, значения перечисляемых типов, специальные константы True, False и nil и выражения, построенные на базе этих элементов с использованием операторов, преобразования типов и конструкторов множеств. Константные выражения не могут включать переменных, указателей и вызовов функций, зa исключаением: </p>
<table valign="top" cellspacing = "2" cellpadding="5" border="1">
<tr><td>Abs</td><td>High</td><td>Low</td><td>Pred</td><td>Succ</td></tr>
<tr><td>Chr</td><td>Length</td><td>Odd</td><td>Round</td><td>Swap</td></tr>
<tr><td>Hi</td><td>Lo</td><td>Ord</td><td>SizeOf</td><td>Trunc</td></tr>
</table>
<p>Это определение константного выражения используется в нескольких местах в спецификации Delphi. Константные выражения требуются для инициализации глобальных переменных, определения поддиапазонных типов, присваивания порядковых номеров значениям в перечисляемых типах, определения значений параметров по умолчанию, записи инструкций case и объявления как чистых, так и типизированных констант.</p>
Примеры константных выражений:
<pre class="brush:pas">
100
'A'
256 - 1
(2.5 + 1) / (2.5 - 1)
'Embarcadero' + ' ' + 'Developer'
Chr(32)
Ord('Z') - Ord('A') + 1</pre>
<br>
<h3>Ресурсные строки</h3>
<p>Ресурсные строки хранятся как ресурсы, линкуются в исполняемый файл или библиотеку и не могут быть изменены без перекомпиляции программы.</p>
<p>Ресурсные строки объявляются как и прочие чистые константы, но ключевое слово const заменяется на resourcestring. Выражение справа от символа "=" должно быть константным выражением и должно возвращать строковое значение. Например:</p>
<pre class="brush:pas">
resourcestring
CreateError = 'Cannot create file %s';
OpenError = 'Cannot open file %s';
LineTooLong = 'Line too long';
ProductName = 'Embarcadero Rocks';
SomeResourceString = SomeTrueConstant;</pre>
<br>
<h3>Типизированные константы</h3>
<p>Типизированные константы, в отличии от чистых констант, могут хранить значения типа массив, запись, процедурного или указательного типа. Типизированные константы не могут включаться в константные выражения.</p>
Объявление типизированной константы выглядит следующим образом:
<pre class="brush:pas">const identifier: type = value</pre>
<p>где identifier – это любой допустимый идентификатор, type – это любой тип, за исключением файлов и вариантов, а value – это выражение типа type. Например:</p>
<pre class="brush:pas">const Max: Integer = 100;</pre>
<p>В большинстве случаев value должно быть константным выражением, но, если тип выражения – это массив, запись, процедурный или указательный тип, действуют особые правила.</p>
<br>
<h3>Константы-массивы</h3>
<p>Чтобы объявить константу-массив следует заключить значения элементов массива в скобки и разделить их запятыми. Значения элементов массива должны представлять собой константные выражения. Например:</p>
<pre class="brush:pas">const Digits: array[0..9] of Char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');</pre>
<p>объявляет типизированную константу с именем Digits, которая содержит массив символов.
<p>Символьные массивы, начинающиеся с нулевого элемента часто представляют собой строки заканчивающиеся нулевым символом. В связи с этим строковые константы могут быть использованы для инициализации символьных массивов. Предыдущее объявление может быть записано проще: </p>
<pre class="brush:pas">const Digits: array[0..9] of Char = '0123456789';</pre>
<p>Для объявления константы-многомерного массива следует заключить каждое размерение массива в отдельные скобки и разделить их запятыми. Например, объявление: </p>
<pre class="brush:pas">type TCube = array[0..1, 0..1, 0..1] of Integer;
const Maze: TCube = (((0, 1), (2, 3)), ((4, 5), (6,7)));</pre>
создает массив с именем Maze, в котором:
<pre class="brush:pas">Maze[0,0,0] = 0
Maze[0,0,1] = 1
Maze[0,1,0] = 2
Maze[0,1,1] = 3
Maze[1,0,0] = 4
Maze[1,0,1] = 5
Maze[1,1,0] = 6
Maze[1,1,1] = 7</pre>
Константы-массивы не могут содержать значения файлового типа на любых уровнях.
<br>
<h3>Константы-записи</h3>
<p>Для объявления константы типа запись нужно определить значение каждого поля следующим образом: fieldName: value. Присваивания значений полям разделяются точками с запятой (;). Значения должны быть константными выражениями. Поля должны быть перечислены в том же порядке, в каком они следуют в объявлении типа записи. Значение для поля-тэга (если такое присутствует) должно быть установлено. Если запись имеет вариантную часть, то значение может быть установлено только для варианта, определенного полем-тэгом.</p>
Примеры:
<pre class="brush:pas">type
TPoint = record
X, Y: Single;
end;
TVector = array[0..1] of TPoint;
TMonth = (Jan, Feb, Mar, Apr, May, Jun, Jul,
Aug, Sep, Oct, Nov, Dec);
TDate = record
D: 1..31;
M: TMonth;
Y: 1900..1999;
end;
const
Origin: TPoint = (X: 0.0; Y: 0.0);
Line: TVector = ((X: -3.1; Y: 1.5), (X: 5.8; Y: 3.0));
SomeDay: TDate = (D: 2; M: Dec; Y: 1960);</pre>
<p>Константы-записи не могут содержать значения файлового типа на любом уровне.</p>
<br>
<h3>Процедурные константы</h3>
<p>Для объявления процедурной константы укажите имя функции или процедуры, совместимой с объявляемым типом константы. Например:</p>
<pre class="brush:pas">function Calc(X, Y: Integer): Integer;
begin
...
end;
type TFunction = function(X, Y: Integer): Integer;
const MyFunction: TFunction = Calc;</pre>
<p>Сделав такое объявление, вы сможете использовать процедурную константу MyFunction при вызове функции:</p>
<pre class="brush:pas">I := MyFunction(5, 7);</pre>
<p>Вы можете присвоить значение nil процедурной константе.</p>
<br>
<h3>Указательные константы</h3>
<p>Когда вы объявляете указательную константу, вы должны инициализировать ее значением, которое может быть вычислено в процессе компиляции по крайней мере как относительный адрес. Есть три способа сделать это: оператор @, nil и (если константа имеет тип Pchar или PWideChar) при помощи строкового литерала. Например, если I – это глобальная переменная типа Integer, вы можете объявить константу следующим образом:</p>
<pre class="brush:pas">const PI: ^Integer = @I;</pre>
<p>Компилятор сможет вычислить ее, поскольку глобальные переменные являются частью сегмента кода. Так же, как и функции или глобальные константы:</p>
<pre class="brush:pas">const PF: Pointer = @MyFunction;</pre>
<p>Поскольку память для строковых литералов выделяется также, как и для глобальных констант, вы можете инициализировать константу типа PChar строковым литералом:</p>
<pre class="brush:pas">const WarningStr: PChar = 'Warning!';</pre>
<br>
<h3>Изменяемые типизированные константы</h3>
<p>Delphi разрешает изменять типизированные константы в том, случае, если вы устанавливаете директиву компиляторка ($J+} или {$WRITEABLECONST ON}.</p>
<p>При включении директивы $J+ вы можете использовать инструкции присваивания для изменения значения типизированных констант, также как и для переменных. Например:</p>
<pre class="brush:pas">{$J+}
const
foo: Integer = 12;
begin
foo := 14;
end.</pre>
Различия между изменяемыми константами и инициализированными переменными:
<ul>
<li>Изменяемые константы могут быть объявлены глобально или локально в процедурах, функциях или методах;</li>
<li>Инициализированные переменные можно объявить только глобально.</li>
<li>При попытке присваивания значения инициализированным переменным внутри процедур или методов возникает ошибка компиляции.</li>
</ul>
</div>Unknownnoreply@blogger.com0