skiminok: (Default)
skiminok ([personal profile] skiminok) wrote2008-12-02 01:54 am
Entry tags:

Delphi Prism: взгляд сквозь новые грани. Часть 2

Я долго думал, с какой статьи начать перевод. Потом, вспомнив, что в основном моя агитация направлена на Дельфистов, принял решение проводить все описания в плане "Delphi for Win32 vs. Delphi Prism", а уже когда материал подобного рода иссякнет, заняться взглядами под другим углом.
Вот в каком порядке переводить раздел Delphi Prism Language, была проблема уже нешуточная. В конце концов, посоветовавшись с товарищами, я решил идти более-менее по логическому порядку, но писать всё-таки не учебник по программированию, а считать, что читают статью люди, имеющие опыт в данной отрасли. Проще говоря, это будет всё тот же "Delphi for Win32 vs. Delphi Prism", упорядоченный от простейших материалов к интереснейшим :-)

Прежде всего стоит сделать важное замечание. Delphi Prism - .NET-язык, то есть полностью CLR-совместимый. Это значит, что любое изменение и любая мелочь языка должны быть построены в строгом соответствии со стандартом M$ - иначе программа даже просто не скомпилируется. Поэтому, когда я буду говорить о проведённых изменениях, очень многие из них я смогу мотивировать только одной фразой: потому что .NET. Это касается и имён конструкторов, и целочисленного деления, и ещё кучки разных тонкостей, каждую из которых попытались или включить в опции совместимости (как с конструкторами), или обыграть так, чтобы для программиста ничего не поменялось, а компилятор сам проводил преобразования в корректный IL (как с границами массивов - в .NET они начинаются с 0, но в Delphi Prism реализовали, конечно же, привычный нам свободный вариант... но об этом в соответствующем месте), или попросту много раз громко сакцентировать внимание на переделке (как с целочисленным делением). По ходу статьи вы встретите следующие пометки:

..., потому что .NET. — особенно краткое оправдание для некоторых изменений.
Несовместимо! — акцент на кардинальных нововведениях.
Совместимо:) — далее следует инструкция по включению обратной совместимости для описываемого пункта.

Вообще говоря, в 99% случаев все синтаксические проблемы по переходу решаются с помощью Oxidizer.

1. Пространства имён.

Начать следует с нескольких концепций .NET. *Ох, чует моё сердце, за время написания цикла статей по Призме я этих концепций расскажу на пол-МСДН, так что даже первый раз увидевшие платформу в глаза смогут свободно писать на ней тяжеловезные фреймворки сразу после изучения основ синтаксиса произвольного языка) Но, может, это и к лучшему. ОК, поехали.*

Понятия "файл" для программиста не существует. Файлы - это сущность чисто физическая, о ней вспоминаешь, когда сохраняешь проект на жёстком диске, а программа должна работать с сущностями разработки, с контекстом и задачей приложения. Поэтому разбиения на модули нет. Есть разбиение на пространства имён (ПИ). Пространство имён распространяется на несколько файлов, имеет название, ключевое слово namespace заменило собою ключевое слово unit, с той лишь разницей, что его название может повторяться в нескольких файлах - тогда это одно и то же ПИ.
Совместимо:) - ключевое слово unit осталось как синоним для namespace.

Delphi Prism полностью поддерживает циклические ссылки, в отличие от Delphi for Win32. То есть приведённый ниже код скомпилируется без проблем:
namespace FirstOne;
interface
uses SecondOne;

type
  TClass1 = class
  private
    C1: Class2;
  end;
//...
implementation
end.

namespace SecondOne;
interface
uses FirstOne;

type
  Class2 = class(Class1)
  end;
//...
implementation
end.
Это возможно благодаря многопроходному компилятору (компилятор Delphi, как известно, - однопроходный). Коджировцы утверждают, что компилятор Призмы, несмотря на многопроходность, не заимствовал фольклорных проблем С++ и потери в скорости не ощущаются - из-за отсутствия понятия "заголовочный файл". Что же, посмотрим на практике.

Название ПИ можно опустить:
namespace;
interface
implementation
end.
В таком случае описанные в нём объекты станут глобальными для всей сборки. Тем не менее, такой приём не рекомендуется.

Даже если вы не указали используемое ПИ в секции uses, то доступ к его элементам всегда можно получить, полностью прописав путь к элементу прямо в коде:
var
  PS: System.Drawing.Printing.PaperSource;
Или:
if PS.Kind = System.Drawing.Printing.PaperSourceKind.Upper then //...
Если же прописали, то, понятное дело, выстраивать эти достойные фантазии гигантомана конструкции нет необходимости:)

Структуру проекта принято разделять на иерархию вложенных ПИ, чтобы получалось нечто вроде namespace System.Text.RegularExpressions. Чтобы использовать сразу все вложенные в какое-то пространство ПИ, можно воспользоваться конструкцией ".*":
uses System.Xml.*;


Кстати говоря: если вы добавляете использование одних и тех же ПИ в каждое пространство вашего проекта, возможно, лучше будет сделать это глобально с помощью "Project Options" в IDE.

2. Главный метод.
Как только что выяснилось в ходе разговора о пространствах имён, понятия "программа" (program) у CLR не существует. Встаёт логичный вопрос: что же тогда служит точкой входа для сборки? Где искать начало выполнения? Ответ: в главном методе.

Несовместимо!
Главный метод имеет имя Main и служит той точкой, с которой начинается выполнение любой программы, будь она консольной, визуальной, или Веб-сервисом (исключение DLL, которая, вообще говоря, не выполняется). Такой метод должен быть хотя бы у одного класса в вашем пространстве имён; если он найден только у одного класса, выполнение автоматически начнётся с данного Main`a, в противном же случае выбирается тот класс, который указан в Project Options.

Главный метод обязательно статичен, то есть, в терминологии Delphi, представляет собою метод класса (class method). Он может не принимать параметров вообще, иметь один строковый параметр либо иметь один параметр - динамический массив строк. В последних двух случаях Main`у передаются аргументы командной строки.

Главный метод может либо не возвращать значения, либо возвращать целое число - код выхода программы.

Ниже привожу (вернее - честно слямзиваю с Prism Wiki) пример главного метода некоей программы:
class method Program.Main(Args: array of String): Integer;
begin
  if Length(Args) <> 1 then begin
    Console.WriteLine('Filename expected!');
    exit 1;
  end;
  RunWith(Args[0]);
  exit 0;
end;
Здесь, чтобы пояснить небольшой кусочек кода, придётся забежать вперёд и сразу рассказать о нескольких мелочных нововведениях:
  • Ключевое слово method означает то, что и означает - любой метод:-) Это функция или процедура, в терминах старого доброго Паскаля... и Делфи, конечно же. Метод может возвращать значение, а может и не возвращать - в этом его преимущество, меняя функцию на процедуру, вам приходилось заодно изменять и ключевое слово в её описании и реализации. Сейчас это неудобство не нужно. Да и термин ООП "метод" лучше объясняет суть явления, чем пришельцы из 70-х "процедура" и "функция".
    Совместимо:) Никто не заставляет писать method, старый синтаксис procedure и function остался в целях совместимости и убран никогда не будет.
  • Ключевое слово exit - дубликат return из языков с Си-подобным синтаксисом. Немедленный выход из метода с возвращением указанного значения соответствующего типа. Или невозвращением - в случае метода-процедуры.
    Несовместимо! Exit у Delphi Prism - ключевое слово языка с соответствующим синтаксисом. Функция Exit() с одним необязательным параметром из Delphi for Win32 2009 осталась в прошлом.
  • Метод WriteLine() класса Console, как нетрудно догадаться, выполняет функции процедуры WriteLn. Впрочем, это уже относится не к Призме, а чисто к FCL, так что лучше MSDN вам никто ничего не объяснит. Тем более что она теперь русская.
В принципе, с этими замечаниями действия, которые выполняет написанный выше Main, становятся очевидными: проверить, передан ли программе ровно один параметр, только тогда начать выполнение основной логики, в противном случае вывести на консоль сообщение об ошибки и вернуть возмущённый код выхода.

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

Совместимо:) Переменные в Призме объявляются стандартной конструкцией var. Другое дело что теперь эта конструкция свободно гуляет по всему вашему коду. В отличие от Delphi for Win32, в Призме объявление переменных доступно не только в описательной секции метода, но и в любой точке его кода. Старый принцип Бейсика и Си: где понадобилось - там и объявляйте. Любить этот принцип или нет, использовать или забыть как страшный сон - решать только вам.
method DoSomething;
var
  someInt1, someInt2: Integer;
  someFloat: Double := 0.5;
begin
  //немного кода
  var someBoolean: Boolean;
  //и ещё немного кода
  var AnswerToLifeTheUniverseAndEverything: Integer := 42;
end;

Несовместимо! Обратите внимание: начальная инициализация переменных происходит не символом "=", а оператором присваивания ":=". По мнению разработчиков Delphi Prism, такой синтаксис корректнее иллюстрирует суть процесса: присваивание стартового значения переменной (полю, свойству... да мало ли), а не статическое, постоянное равенство. Это же вам не константа.

Инициализация переменных одновременно с объявлением - возможность, доступная для любого типа данных. Вот вам создание объекта:
method DeclareTwoFiles;
var
  File1: System.IO.FileStream := new FileStream('c:\file1.txt', FileMode.Append, FileAccess.Write, FileShare.Write);
begin
  var File2: System.IO.FileStream := new FileStream('c:\file2.txt', FileMode.Append, FileAccess.Write, FileShare.Write);
end;
Да, господа, это вызов конструктора. Вот во что он превратился. Конструкторы в Призме безымянные, вернее сказать, их имя совпадает с именем класса, потому что .NET. Но я дарю вам опять волю к жизни и ставлю яркую пометочку Совместимо:) - возможность использованния конструкторов вида .Create включается в Project Options, в настройках обратной совместимости. Но об этом - в своё время, ещё дойдёт у нас речь до конструкторов.

Delphi Prism, будучи дотнет-языком, активно использует автоматическое распознавание типов, более известное как Type Inference:
var x := %101;
var s := 'Hello';
var c := new System.UTF8Encoding(true);
Здесь переменная x сама по себе получит тип Byte (как наименьший из возможных), переменная s - тип string, а c - UTF8Encoding. К сожалению, в настоящий момент это работает только для локальных переменных, - но в том числе и для объявленных "по месту".
(Кстати, вы заметили "%101"? Это просто пятёрка. В двоичной системе счисления, что указано соответствующим префиксом. Да и старый знакомый префикс "$" для шестнадцатеричной тоже никуда не делся.)

Любой идентификатор может быть зарезервированным стандартным словом, если не забывать везде ставить перед ним символ "&":
var &End: Boolean;
Более того, когда идентификатор, как название члена, следует после точки, амперсанд и тот можно опустить - компилятор Призмы понимает, что здесь может быть только пользовательское название, но никак не ключевое слово языка.

Что-то статья получилась о ерунде всякой. Пожалуй, на третью часть надо пустить что-нибудь действительно покрасивей: паралелльные циклы, там... или асинхронное выполнение. Или про обнуляемые типы можно поговорить. Посмотрим через пару дней.

(Anonymous) 2008-12-02 12:48 pm (UTC)(link)
Отличный материал, спасибо.

[identity profile] ionflux.livejournal.com 2008-12-02 02:07 pm (UTC)(link)
+1

[identity profile] anton-yu-b.livejournal.com 2008-12-02 08:26 pm (UTC)(link)
Спасибо! Ждем продолжения :)

[identity profile] maks-s.livejournal.com 2008-12-03 12:00 am (UTC)(link)
очень интересно. распространю среди жильцов моего ЖЭКа )

(Anonymous) 2009-10-04 10:40 am (UTC)(link)
Спасибо вам за труд, ждём продолжения.

cross-platform

[identity profile] vahan av (from livejournal.com) 2011-03-29 08:17 am (UTC)(link)
На сайте embarcadero в описании Delphi Prism XE есть такая вот строчка
Build cross-platform Mono applications and deploy on Windows, Linux and Mac OS X
Кто то пробовал делать приложеня под Linux или Mac OS X на Prism-е?