Технология Клиент-Сервер 2003'4 |
|||||||
|
Платформа создания приложений .NET, активно продвигаемая Microsoft, находит все больше приверженцев среди программистов благодаря удобству, гибкости и широким возможностям. Понятно, что и другие производители средств разработки, например, Borland, не собираются оставаться в стороне от этого. Первый шаг в завоевании симпатий .NET-программистов фирма Borland сделала, создав C# Builder, продукт для разработки .NET-приложений на языке C#. Однако Borland не остановился на этом. Совсем недавно фирмой была представлена восьмая версия популярной среды разработки Delphi. Слухи о новых возможностях восьмой версии давно будоражили умы разработчиков, бета-версии Delphi демонстрировались на различных презентациях, таких как Borland Developer Day, однако обстоятельно поработать с Delphi 8 все никак не удавалось. Наконец, Delphi 8 вышла в свет, что дало возможность «пощупать» продукт и поделиться с читателями свежими впечатлениями. При написании статьи использовалась версия Delphi Architect.
Прежде всего стоит отметить, что Delphi 8 предназначена только для создания приложений для .NET. Однако, по заявлениям Borland, вместе с восьмой версией будет поставляться и предыдущая, седьмая версия, ориентированная на разработку Win32 приложений.
Первое, что бросается в глаза при запуске новой версии Delphi – это новый стиль IDE (Integrated Development Enviroment). Фирма Borland активно унифицирует IDE своих продуктов. Новая IDE Delphi аналогична IDE C# Builder.
Внесенные изменения носят не только и не столько косметический характер. Фирма Borland, как и обещала, переходит от использования IDE к UDE (Unified Development Enviroment) – унифицированной среде разработки. О многом говорит хотя бы тот факт, что для новых продуктов C# Builder и Delphi выделена отдельная папка Program Files\Borland\BDS. C# устанавливается в подпапку 1.0, а Delphi – в 2.0. Использование UDE дает несколько преимуществ:
Большинство привычных пунктов меню и палитр либо остались на старых местах, либо быстро обнаруживаются визуально, не вызывая дискомфорта у программистов, уже работавших в предыдущих версиях Delphi. Было добавлено несколько новых возможностей, часть из них обусловлена спецификой .NET, а часть дальнейшим повышением удобства IDE.
Наличие докируемых окон редактора свойств, менеджера проекта и палитры компонентов позволяет значительно повысить удобство работы с редактором за счет автоматического сворачивания неиспользуемых окон. При этом освободившаяся часть экрана продуктивно используется активным окном.
Редактор кода также обзавелся новыми возможностями. Самое заметное и весьма удобное дополнение – возможность минимизировать блок текста. В качестве блоков текста выступают: секции декларации (interface) и реализации (implementation), декларация класса, текст метода класса и т. д.
Возможность быстрой записи клавиатурного макроса позволяет запоминать и повторно вызывать последовательность нажатия кнопок в редакторе кода. К сожалению, средства создания клавиатурных макросов пока не развиты, так можно записать только один макрос.
Еще одним приятным нововведением редактора стала возможность настройки подсветки синтаксиса. Поддерживается подсветка языков Delphi, C#, C++, XML/HTML, кроме того, возможно настроить опции редактирования для каждого из языков.
Окно шаблонов кода (CodeSnippets) позволяет создавать повторно используемые шаблоны исходного кода. Такая возможность была и ранее, однако теперь CodeSnippets вынесены в отдельное окно, и пользоваться ими, на мой взгляд, стало удобнее.
Теперь немного о грустном. Как и ранее, в редакторе есть возможность выбора привязки клавиш из предопределенного списка. Однако чтобы определить собственные сочетания клавиш для вызова функций редактора, необходимо писать модуль расширения среды. Такой подход кажется неудобным и нелогичным, хотя возможно, мы чего-то не понимаем.
Не может не радовать добавление возможностей интеграции с системами контроля версий. У предыдущих версий Delphi встроенные возможности работы с системами поддержки версий были довольно слабы, что приводило к необходимости использовать для решения этой задачи продукты третьих фирм. В восьмой версии реализована поддержка взаимодействия с системами контроля версий через Microsoft SCC API. Поддерживаются следующие популярные системы контроля версий:
Изменения, обусловленные спецификой .NET, не вызывают серьезных вопросов у программистов, уже знакомых с этой технологией, поэтому в этом обзоре мы не будем подробно на них останавливаться.
Delphi 8 позволяет разрабатывать приложения WinForms, ASP.NET, WebServices, компоненты для WinForms и Web с использованием всех возможностей .NET Framework. Другими словами, Delphi8 позволяет разрабатывать все виды .NET-приложений.
Кроме того, на платформу .NET была портирована родная объектная библиотека VCL, получившая имя VCL.NET. Перенесен также набор компонентов Indy для работы с сетью и генератор отчетов RaveReports.
Дальнейшее развитие получила технология создания приложений Bold. Bold был портирован на платформу .NET и получил название ECO (Enterprise Core Object).
Таким образом, помимо использования всех возможностей .NET, возможно относительно быстро перенести под .NET большую часть старого кода Delphi, написанного для Win32.
В состав Delphi входит рефлектор, позволяющий получать метаинформацию об объектах сборки .NET.
Необходимость поддержки возможностей .NET привела к значительному изменению языка Delphi. Ниже приведено краткое описание внесенных в язык изменений.
Стандартным контейнером типов в Delphi является юнит (unit). В .NET реализована другая система организации хранения типов – пространства имен. Необходимость работы с классами .NET потребовала введения в Delphi поддержки пространства имен. В отличие от юнитов, пространства имен имеют иерархическую структуру. Наличие иерархии пространства имен позволяет исключить неоднозначность в определении типов данных, имеющих одинаковое текстовое имя.
Вот что сказано в документации о пространствах имен.
В Delphi 8 файл проекта неявно объявляет новое пространство имен, называемое пространством имен проекта по умолчанию (project default namespace).
Например строка:
program MyProgram;
|
Объявляет пространство имен MyProgram.
Юнит может иметь пространство имен, вложенное в пространство проекта, или явно объявить собственное пространство имен. Пространство имен юнита объявляется в его заголовке, следующий код объявляет пространство имен MyUnit:
unit MyUnit;
|
Поддержка иерархии пространства имен обеспечивается разделением точкой частей иерархии:
unit MyNamespace.MyUnit;
|
В данном примере объявлено пространство имен MyUnit, вложенное в пространство имен MyNamespace. При этом файл, в который будет сохранен юнит, будет называться MyNamespace.MyUnit.pas. Глубина иерархии пространства имен не ограничена.
По умолчанию, если имя юнита не содержит полного пути иерархии, то он считается вложенным в пространство имен проекта. При этом в имя файла модуля не добавляется полный путь пространства имен проекта. Однако результатом компиляции такого модуля станет файл, содержащий в имени полный путь иерархии и имеющий расширение .cduil. Рассмотрим вышесказанное на простом примере. Допустим, что у нас есть проект
program MyProgram;
|
содержащий юнит
unit MyUnit;
|
Тогда юнит будет иметь пространство имен MyProgram.MyUnit, файл юнита будет называться MyUnit1.pas, а компилироваться модуль будет в файл MyProgram.MyUnit.dcuil.
Регистр символов в имени пространства имен не учитывается.
Пространство имен может иметь алиас – короткое название, объявленный после ключевого слова as:
uses MyCompany.AVeryLongNamespaceDesignation.VeryDescriptiveUnitName as aUnit; |
В данной декларации объявлен алиас aUnit для пространства имен MyCompany.AVeryLongNamespaceDesignation.VeryDescriptiveUnitName.
При сборке проекта компилятор использует следующий порядок поиска пространств имен:
А теперь скажем о результатах наших маленьких экспериментов.
К сожалению, мы так и не смогли воспользоваться ключевым словом namespaces. Компилятор просто не воспринимал его. Похоже, что в рассматриваемом билде Delphi (7.1.1446.610) просто забыли включить поддержку этой возможности.
Второй тонкий момент использования пространств имен, несколько озадачивший нас – автоматическое включение модуля в пространство имен проекта. Так, в документации утверждается, что если имя модуля не содержит полного имени пространства имен, то оно включается в пространство имен проекта. Мы создали простой проект WinFormsApplication, содержащий файл проекта Project1 и одну форму. Заголовок юнита формы unit WinForm; Казалось бы, что в соответствии с документацией классы юнита должны содержаться в пространстве имен Project1.WinForm. При этом файл winform.pas должен быть скомпилирован в файл project1.winform.dcuil. Мы никак не могли понять, а что же нам делать, если мы хотим чтобы классы юнита не включались в пространство имен пректа, а были корневым элементом нового пространства имен WinForm. А что случится, если создать еще один юнит с формой и назвать его project1.winform.pas, не будет ли конфликта имен?
Результат был таков: модуль с именем WinForm не включается в пространство имен проекта по умолчанию. Он генерирует свое собственное пространство имен. В файле проекта содержится строка, явно указывающая на это:
WinForm in 'WinForm.pas' {WinForm.TWinForm: System.Windows.Forms.Form} |
Модуль компилируется в файл winform.pas и никак не конфликтует с классами модуля unit Project1.WinForm;
В общем, кому верить? Своим глазам или документации от Borland? Мы не пока не знаем.
Объектная модель Delphi ранее позволяла получать доступ к protected- и private-членам класса из других классов, объявленных в том же модуле. Модификатор strict, появившийся в восьмой версии запрещает такое поведение. Например, объявив класс, мы говорим Delphi, что процедура Dispose не может быть вызвана другими классами, даже если они будут объявлены в том же модуле:
TWinForm = class(System.Windows.Forms.Form) strict private strict protected procedure Dispose(Disposing: Boolean); override; end; |
Ранее можно было объявлять в классе лишь статические методы класса (class method), теперь объявить статическим можно поле, и даже свойство. Ниже приведены примеры такого объявления.
type TMyClass = class strict private class var FX: Integer; strict protected // Accessor-ы для свойств класса должны быть объявлены как class static function GetX: Integer; static; procedure SetX(val: Integer); static; public class property X: Integer read GetX write SetX; procedure StatProc(s: String); static; end; |
Статический конструктор класса выполняется перед тем, как класс будет использован. Он должен быть объявлен в секции strict private. Класс имеет только один статический конструктор без параметров потому, что он вызывается CLR при первом обращении к типу. При наследовании не нужно в теле статического конструктора вызывать статический конструктор предка, используя inherited. Компилятор это сделает за вас сам. Пример объявления статического конструктора:
type TMyClass = class strict private class var class constructor Create; end; |
Введена поддержка вложенных типов, вложенные типы являются частью .NET. Синтаксис объявления вложенного типа:
type className = class [abstract | sealed] (ancestorType) memberList type nestedTypeDeclaration memberList end; |
Для поддержки классов .NET с запретом наследования было добавлено ключевое слово sealed. Класс, объявленный как sealed, не может иметь наследников. Синтаксис объявления:
type className = class [abstract | sealed] (ancestorType) memberList end; |
Виртуальный метод может быть объявлен с модификатором final. Такие методы не могут быть перекрыты в потомках класса.
В Delphi 8 появилась возможность перегрузки операторов . Для того, чтобы перегрузить оператор, нужно реализовать функцию с определенной сигнатурой. В таблице 1 приведен список операторов, которые можно переопределить, и сигнатуры функций, которые для этого нужно реализовать.
Symbol Mapping | Сигнатура | Категория |
---|---|---|
implicit typecast | Implicit(a : type) : resultType; | Приведение |
explicit typecast | Explicit(a: type) : resultType; | Приведение |
- | Negative(a: type) : resultType; | Унарный |
+ | Positive(a: type): resultType; | Унарный |
Inc | Inc(a: type) : resultType; | Унарный |
Dec | Dec(a: type): resultType | Унарный |
not | LogicalNot(a: type): resultType; | Унарный |
not | BitwiseNot(a: type): resultType; | Унарный |
Trunc | Trunc(a: type): resultType; | Унарный |
Round | Round(a: type): resultType; | Унарный |
= | Equal(a: type; b: type) : Boolean; | Сравнение |
<> | NotEqual(a: type; b: type): Boolean; | Сравнение |
> | GreaterThan(a: type; b: type) Boolean; | Сравнение |
>= | GreaterThanOrEqual(a: type; b: type): resultType; | Сравнение |
< | LessThan(a: type; b: type): resultType; | Сравнение |
<= | LessThanOrEqual(a: type; b: type): resultType; | Сравнение |
+ | Add(a: type; b: type): resultType; | Бинарный |
- | Subtract(a: type; b: type) : resultType; | Бинарный |
* | Multiply(a: type; b: type) : resultType; | Бинарный |
/ | Divide(a: type; b: type) : resultType; | Бинарный |
div | IntDivide(a: type; b: type): resultType; | Бинарный |
mod | Modulus(a: type; b: type): resultType; | Бинарный |
shl | ShiftLeft(a: type; b: type): resultType; | Бинарный |
shr | ShiftRight(a: type; b: type): resultType; | Бинарный |
and | LogicalAnd(a: type; b: type): resultType; | Бинарный |
or | LogicalOr(a: type; b: type): resultType; | Бинарный |
xor | LogicalXor(a: type; b: type): resultType; | Бинарный |
and | BitwiseAnd(a: type; b: type): resultType; | Бинарный |
or | BitwiseOr(a: type; b: type): resultType; | Бинарный |
xor | BitwiseXor(a: type; b: type): resultType; | Бинарный |
Как и Delphi, .NET обладает мощной инфраструктурой метаданных. Любое приложение или библиотека .NET содержит полное описание классов, их методов и т.п. Доступ к этой информации можно получит с помощью удобного API Reflection. Но разработчики .NET не остановились на этом, и реализовали возможность расширения метаданных с помощью так называемых атрибутов. .NET-программисты с успехом применяют метаинформацию для упрощения кода своих приложений, заменяя объемный и непонятный код простыми и легко читаемыми декларациями атрибутов. Поскольку Delphi 8 изначально преподносилась как Delphi.NET, в ней просто не могли обойтись без поддержки атрибутов .NET.
Атрибут объявляется как обычный класс, наследник TCustomAttribute, например:
type TCustomCodeAttribute = class(TCustomAttribute) private Fprop1 : integer; Fprop2 : integer; aVal : integer; procedure Setprop1(p1 : integer); procedure Setprop2(p2 : integer); public constructor Create(const myVal : integer); property prop1 : integer read Fprop1 write Setprop1; property prop2 : integer read Fprop2 write Setprop2; end; |
Конструктор атрибута будет в нашем примере выглядеть так:
constructor TCustomCodeAttribute.Create(const myVal : integer); begin inherited Create; aVal := myVal; end; |
А воспользоваться атрибутом можно следующим образом:
[TCustomCodeAttribute(1024, prop1=512, prop2=128)] TMyClass = class(TObject) ... end; |
Необходимость введения хелперов класса как элемента языка обусловлена желанием Borland перенести объектную библиотеку VCL на .NET. Как известно, в VCL базовым классом является TObject. В .NET ему соответствует класс Object. Однако класс Object хотя и является аналогом TObject, реализует далеко не все методы и свойства последнего. Многие классы из VCL так же имеют аналоги в .NET, частично реализующие функциональность VCL класса.
Конечно, можно было бы написать наследников .NET-классов, однако гораздо удобнее «подмешать» необходимую функциональность в .NET класс, не вникая в тонкости внутреннего устройства .NET класса. Именно этим и занимается хелпер. Если какой-либо член класса (свойство, метод, поле) не найден в основном классе, то делается попытка найти его в хелпере. Синтаксис объявления хелпера
type identifierName = class helper [(ancestor list)] for classTypeIdentifierName memberList end; |
Продемонстрируем использование хелпера на простом примере:
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 // Self относится к типу TMyClass, а не TMyClassHelper writeln(Self.ClassName); end; function TMyClassHelper.MyFunc: Integer; begin ... end; ... var X: TMyClass; begin X := TMyClass.Create; X.MyProc; // Calls TMyClass.MyProc X.HelloWorld // Calls TMyClassHelper.HelloWorld X.MyFunc; // Calls TMyClassHelper.MyFunc end; |
В данном примере объявляется класс TMyClass, содержащий процедуру MyProc и функцию MyFunc. Для класса TMyClass объявлен хелпер TMyClassHelper, содержащий процедуру HelloWorld и функцию MyFunc. Далее в коде создается экземпляр класса TMyClass и вызываются методы MyProc, HelloWorld, MyFunc. Процедура MyProc вызывается из класса, а два оставшихся метода из хелпера.
Фактически Delphi.NET имеет две объектные библиотеки: стандартную библиотеку классов .NET – FCL и перенесенную на платформу .NET VCL. Можно создавать приложения, используя одну из этих библиотек компонентов. Если с FCL более и менее все понятно, то о VCL.NET стоит рассказать подробнее.
VCL.NET предоставляет тот же набор компонентов, что и старая добрая VCL. Borland приложила все усилия, чтобы старые проекты, написанные на Delphi легко переносились на платформу .NET. Пока рано говорить, насколько беспроблемно будет происходить перенос кода, однако при беглом взгляде глобальных проблем не видно. Помимо переноса старого кода приложений, Borland предусмотрела возможность импорта компонентов .NET в приложения VCL.NET. При этом создается обертка вокруг .NET-компонента, позволяющая использовать его в приложении VCL.NET.
Одним из очень удобных нововведений Delphi 8 является возможность визуализации диаграмм классов приложения.
Достаточно просто взглянуть на скриншот, и подробные комментарии будут излишни. Буквально с первых минут использования визуальных диаграмм начинаешь понимать, насколько удобно визуализировать иерархии классов.
В составе Delphi, помимо стандартных провайдеров доступа к данным, входящих в состав .NET Framework, поставляются провайдеры от Borland, позволяющие работать с такими популярными серверами БД как:
Oracle
MS SQL
MS Access
Interbase
DB2
Общая архитектура доступа к данным в Delphi 8 показана на рисунке.
Как видно из диаграммы, провайдеры от Borland (BDP – Borland Data Providers), работают с сервером напрямую, минуя OLE DB. Теоретически это позволяет повысить скорость работы, однако до тех пор, пока не будут проведены компетентные тесты, давать какие-то заключения преждевременно.
За столь короткое время невозможно досконально изучить все нововведения, и данный обзор не претендует на исчерпывающую полноту. Однако мы постарались отметить большинство ключевых моментов, связанных с Delphi 8.
Несмотря на отдельные недостатки, новый продукт Borland производит приятное впечатление. По нашему мнению, Borland в очередной раз подтвердила свою репутацию одного из ведущих поставщиков средств разработки. Надеемся, что Delphi 8 займет достойное место среди инструментария разработки .NET приложений.
Copyright © 1994-2016 ООО "К-Пресс"