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. То есть приведённый ниже код скомпилируется без проблем:
Название ПИ можно опустить:
Даже если вы не указали используемое ПИ в секции uses, то доступ к его элементам всегда можно получить, полностью прописав путь к элементу прямо в коде:
Структуру проекта принято разделять на иерархию вложенных ПИ, чтобы получалось нечто вроде namespace System.Text.RegularExpressions. Чтобы использовать сразу все вложенные в какое-то пространство ПИ, можно воспользоваться конструкцией ".*":
Кстати говоря: если вы добавляете использование одних и тех же ПИ в каждое пространство вашего проекта, возможно, лучше будет сделать это глобально с помощью "Project Options" в IDE.
2. Главный метод.
Как только что выяснилось в ходе разговора о пространствах имён, понятия "программа" (program) у CLR не существует. Встаёт логичный вопрос: что же тогда служит точкой входа для сборки? Где искать начало выполнения? Ответ: в главном методе.
Несовместимо!
Главный метод имеет имя Main и служит той точкой, с которой начинается выполнение любой программы, будь она консольной, визуальной, или Веб-сервисом (исключение DLL, которая, вообще говоря, не выполняется). Такой метод должен быть хотя бы у одного класса в вашем пространстве имён; если он найден только у одного класса, выполнение автоматически начнётся с данного Main`a, в противном же случае выбирается тот класс, который указан в Project Options.
Главный метод обязательно статичен, то есть, в терминологии Delphi, представляет собою метод класса (class method). Он может не принимать параметров вообще, иметь один строковый параметр либо иметь один параметр - динамический массив строк. В последних двух случаях Main`у передаются аргументы командной строки.
Главный метод может либо не возвращать значения, либо возвращать целое число - код выхода программы.
Ниже привожу (вернее - честно слямзиваю с Prism Wiki) пример главного метода некоей программы:
3. Переменные.
Небольшое отступление от логики. Вообще говоря, повторюсь, если бы это был учебник по программированию на Delphi Prism для начинающих, то я бы пошёл как установлено временем: идентификаторы, выражения, константы, простейшие операторы... а только потом уже такая сложнейшая по своему наполнению область, как использование переменных. Но поскольку статью читают (надеюсь, читают) далеко не нубы, то я перескочу сразу к более интересному материалу, а ценные сведения об основах упомяну тогда, когда они срочно понадобятся по ходу рассказа.
Совместимо:) Переменные в Призме объявляются стандартной конструкцией var. Другое дело что теперь эта конструкция свободно гуляет по всему вашему коду. В отличие от Delphi for Win32, в Призме объявление переменных доступно не только в описательной секции метода, но и в любой точке его кода. Старый принцип Бейсика и Си: где понадобилось - там и объявляйте. Любить этот принцип или нет, использовать или забыть как страшный сон - решать только вам.
Несовместимо! Обратите внимание: начальная инициализация переменных происходит не символом "=", а оператором присваивания ":=". По мнению разработчиков Delphi Prism, такой синтаксис корректнее иллюстрирует суть процесса: присваивание стартового значения переменной (полю, свойству... да мало ли), а не статическое, постоянное равенство. Это же вам не константа.
Инициализация переменных одновременно с объявлением - возможность, доступная для любого типа данных. Вот вам создание объекта:
Delphi Prism, будучи дотнет-языком, активно использует автоматическое распознавание типов, более известное как Type Inference:
(Кстати, вы заметили "%101"? Это просто пятёрка. В двоичной системе счисления, что указано соответствующим префиксом. Да и старый знакомый префикс "$" для шестнадцатеричной тоже никуда не делся.)
Любой идентификатор может быть зарезервированным стандартным словом, если не забывать везде ставить перед ним символ "&":
Что-то статья получилась о ерунде всякой. Пожалуй, на третью часть надо пустить что-нибудь действительно покрасивей: паралелльные циклы, там... или асинхронное выполнение. Или про обнуляемые типы можно поговорить. Посмотрим через пару дней.
Вот в каком порядке переводить раздел 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, как известно, - однопроходный). Коджировцы утверждают, что компилятор Призмы, несмотря на многопроходность, не заимствовал фольклорных проблем С++ и потери в скорости не ощущаются - из-за отсутствия понятия "заголовочный файл". Что же, посмотрим на практике.interface
uses SecondOne;
type
TClass1 = class
private
C1: Class2;
end;
//...
implementation
end.
namespace SecondOne;
interface
uses FirstOne;
type
Class2 = class(Class1)
end;
//...
implementation
end.
Название ПИ можно опустить:
namespace;
interface
implementation
end.
В таком случае описанные в нём объекты станут глобальными для всей сборки. Тем не менее, такой приём не рекомендуется.interface
implementation
end.
Даже если вы не указали используемое ПИ в секции uses, то доступ к его элементам всегда можно получить, полностью прописав путь к элементу прямо в коде:
var
PS: System.Drawing.Printing.PaperSource;
Или: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;
Здесь, чтобы пояснить небольшой кусочек кода, придётся забежать вперёд и сразу рассказать о нескольких мелочных нововведениях: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 вам никто ничего не объяснит. Тем более что она теперь русская.
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;
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, в настройках обратной совместимости. Но об этом - в своё время, ещё дойдёт у нас речь до конструкторов.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;
Delphi Prism, будучи дотнет-языком, активно использует автоматическое распознавание типов, более известное как Type Inference:
var x := %101;
var s := 'Hello';
var c := new System.UTF8Encoding(true);
Здесь переменная x сама по себе получит тип Byte (как наименьший из возможных), переменная s - тип string, а c - UTF8Encoding. К сожалению, в настоящий момент это работает только для локальных переменных, - но в том числе и для объявленных "по месту".var s := 'Hello';
var c := new System.UTF8Encoding(true);
(Кстати, вы заметили "%101"? Это просто пятёрка. В двоичной системе счисления, что указано соответствующим префиксом. Да и старый знакомый префикс "$" для шестнадцатеричной тоже никуда не делся.)
Любой идентификатор может быть зарезервированным стандартным словом, если не забывать везде ставить перед ним символ "&":
var &End: Boolean;
Более того, когда идентификатор, как название члена, следует после точки, амперсанд и тот можно опустить - компилятор Призмы понимает, что здесь может быть только пользовательское название, но никак не ключевое слово языка.Что-то статья получилась о ерунде всякой. Пожалуй, на третью часть надо пустить что-нибудь действительно покрасивей: паралелльные циклы, там... или асинхронное выполнение. Или про обнуляемые типы можно поговорить. Посмотрим через пару дней.
no subject
(Anonymous) 2008-12-02 12:48 pm (UTC)(link)no subject
no subject
no subject
no subject
(Anonymous) 2009-10-04 10:40 am (UTC)(link)cross-platform
Build cross-platform Mono applications and deploy on Windows, Linux and Mac OS X
Кто то пробовал делать приложеня под Linux или Mac OS X на Prism-е?