Oxygene (ранее известный как Chrome ) — язык программирования , разработанный RemObjects Software для Microsoft Common Language Infrastructure , Java Platform и Cocoa . Oxygene основан на Delphi Object Pascal , но также имеет влияние C# , Eiffel , Java , F# и других языков.
По сравнению с ныне устаревшим Delphi.NET , Oxygene не делает акцент на полной обратной совместимости, а призван стать «переосмыслением» языка, стать полноправным членом управляемых платформ разработки и использовать все функции и технологии, предоставляемые средами выполнения .NET и Java.
Oxygene — это коммерческий продукт, который предлагает полную интеграцию в среду разработки Visual Studio IDE от Microsoft на Windows, а также собственную среду разработки Fire для использования на macOS . Oxygene — один из шести языков, поддерживаемых базовым набором инструментов Elements Compiler, наряду с C# , Swift , Java , Go и Mercury (на основе Visual Basic.NET ).
С 2008 по 2012 год RemObjects Software лицензировала свой компилятор и технологию IDE компании Embarcadero для использования в их продукте Embarcadero Prism . [2] Начиная с осени 2011 года Oxygene стал доступен в двух отдельных редакциях, причем во вторую редакцию была добавлена поддержка сред выполнения Java и Android. Начиная с выпуска XE4, Embarcadero Prism больше не является частью SKU RAD Studio. Для клиентов Prism существует множество путей поддержки и обновления для перехода на Oxygene. [3] По состоянию на 2016 год существует только одна редакция Oxygene, которая позволяет разрабатывать на Windows или macOS и может создавать исполняемые файлы для Windows, Linux, WebAssembly .NET, iOS, Android, Java и macOS.
Язык Oxygene берет свое начало в Object Pascal в целом и Delphi в частности, но был разработан для отражения принципов программирования .NET и создания полностью совместимых с CLR сборок. Поэтому некоторые незначительные языковые возможности, известные из Object Pascal / Delphi, были удалены или пересмотрены, в то время как множество новых и более современных возможностей, таких как Generics или Sequences и Queries, были добавлены в язык.
Oxygene — это объектно-ориентированный язык, что означает, что он использует классы, которые могут хранить данные и выполнять код, для проектирования программ. [ необходимо пояснение ] Классы — это «прототипы» объектов, как идея яблока — прототип яблока, которое можно купить в магазине. Известно, что у яблока есть цвет и что его можно очистить: это данные и исполняемый «код» для класса яблока.
Oxygene обеспечивает поддержку на уровне языка для некоторых функций параллельного программирования. Цель состоит в том, чтобы использовать все ядра или процессоры компьютера для повышения производительности. Чтобы достичь этой цели, задачи должны быть распределены между несколькими потоками. Класс .NET Framework предложил ThreadPool
способ эффективной работы с несколькими потоками. Библиотека параллельных задач (TPL) была представлена в .NET 4.0 для предоставления дополнительных функций для параллельного программирования.
Операторы можно перегружать в Oxygene, используя следующий class operator
синтаксис:
оператор класса неявный ( i : Integer ) : MyClass ;
Обратите внимание, что для перегрузки операторов каждый оператор имеет имя, которое должно использоваться в синтаксисе перегрузки операторов, поскольку, например, «+» не будет допустимым именем метода в Oxygene. [4]
Oxygene не использует "Units", как Delphi, а использует пространства имен .NET для организации и группировки типов. Пространство имен может охватывать несколько файлов (и сборок), но один файл может содержать типы только одного пространства имен. Это пространство имен определяется в самом верху файла:
пространство имен ConsoleApplication1;
Файлы Oxygene разделены на раздел интерфейса и раздел реализации, который является структурой, известной из Delphi. Раздел интерфейса следует за объявлением пространства имен. Он содержит uses
предложение, которое в Oxygene импортирует типы из других пространств имен:
использует System.Linq ;
Импортированные пространства имен должны быть в самом проекте или в ссылочных сборках. В отличие от C#, в Oxygene псевдонимы не могут быть определены для пространств имен, только для имен отдельных типов (см. ниже).
После uses
предложения файл содержит объявления типов, как они известны из Delphi:
интерфейстип ConsoleApp = класс публичный метод класса Main ; конец ;
Как и в C#, метод Main является точкой входа для каждой программы. Он может иметь параметр args : Array of String
для передачи аргументов командной строки в программу.
Больше типов можно объявить без повторения type
ключевого слова.
Реализация заявленных методов размещена в разделе реализации:
выполнениеclass method ConsoleApp.Main ; begin // добавьте сюда свой код Console.WriteLine ( ' Hello World. ' ) ; end ; конец .
Файлы всегда заканчиваются наend.
Как язык .NET, Oxygene использует систему типов .NET: существуют типы значений (например, структуры) и ссылочные типы (например, массивы или классы).
Хотя Oxygene не вводит собственные "предопределенные" типы, он предлагает более "паскалевские" общие имена для некоторых из них, [5] так, например, System.Int32
может использоваться как Integer
и Boolean
( System.Boolean
), Char
( System.Char
), Real
( System.Double
) также присоединяются к семейству pascal-typenames. Структурный характер этих типов, который является частью .NET, полностью сохранен.
Как и во всех языках .NET, типы в Oxygene имеют видимость. В Oxygene видимость по умолчанию — assembly
, что эквивалентно internal
видимости в C#. Другая возможная видимость типа — public
.
тип MyClass = публичный класс конец ;
Видимость можно настроить для каждого определенного типа (классы, интерфейсы, записи, ...).
Для типов можно определить псевдоним, который можно использовать локально или в других сборках Oxygene.
type IntList = public List < Integer >; //видим в других сборках Oxygene SecretEnumerable = IEnumerable < String >; //не виден в других сборках
Псевдонимы публичных типов не будут видны для других языков.
Записи — это то, как структуры .NET называются в Oxygene. Они объявляются так же, как классы, но с record
ключевым словом:
тип MyRecord = метод записи Foo ; конец ;
Поскольку записи представляют собой всего лишь структуры .NET, они могут иметь поля, методы и свойства, но не имеют наследования и не могут реализовывать интерфейсы .
Интерфейсы — очень важная концепция в мире .NET, сама структура активно их использует. Интерфейсы — это спецификация небольшого набора методов, свойств и событий, которые класс должен реализовать при реализации интерфейса. Например, интерфейс IEnumerable<T>
определяет GetEnumerator
метод, который используется для итерации последовательностей.
Интерфейсы объявляются так же, как классы:
тип MyInterface = метод открытого интерфейса MakeItSo : IEnumerable ; свойство Bar : String чтение запись ; конец ;
Обратите внимание, что для свойств геттер и сеттер явно не указаны.
Делегаты определяют сигнатуры для методов, так что эти методы могут быть переданы в параметрах (например, обратных вызовах) или сохранены в переменных и т. д. Они являются типобезопасным эквивалентом NET указателей функций. Они также используются в событиях. При назначении метода делегату необходимо использовать оператор @
, чтобы компилятор знал, что нужно не вызывать метод, а просто назначить его.
Oxygene может создавать анонимные делегаты; например, методы можно передавать методу Invoke
элемента управления без объявления делегата:
метод MainForm.MainForm_Load ( sender : System.Object ; e : System.EventArgs ) ; begin Invoke ( @DoSomething ) ; end ;
DoSomething
Компилятор создаст анонимный делегат с сигнатурой метода .
Oxygene поддерживает полиморфные делегаты, что означает, что делегаты, имеющие параметры нисходящих типов, совместимы по назначению. Предположим, что два класса MyClass
и MyClassEx = class(MyClass)
, тогда в следующем коде BlubbEx
есть совместимое по назначению с Blubb
.
тип делегат Blubb ( отправитель : Объект ; m : MyClass ) ; делегат BlubbEx ( отправитель : Объект ; mx : MyClassEx ) ;
Поля можно использовать для делегирования реализации интерфейса, если тип, к которому они относятся, реализует этот интерфейс:
Реализатор = публичный класс ( IMyInterface ) // ... реализуем интерфейс ... конец ; MyClass = public class ( IMyInterface ) fSomeImplementor : Implementor ; public implements IMyInterface ; //заботится о реализации интерфейса end ;
В этом примере компилятор создаст публичные методы и свойства в MyClass
, которые вызывают методы/свойства fSomeImplementor
, для реализации членов IMyInterface. Это можно использовать для предоставления функциональности типа mixin. [6]
Анонимные методы реализуются внутри других методов. Они не доступны за пределами метода, если только не хранятся внутри поля делегата. Анонимные методы могут использовать локальные переменные метода, в котором они реализованы, и поля класса, к которому они принадлежат.
Анонимные методы особенно полезны при работе с кодом, который должен выполняться в потоке графического интерфейса пользователя, что в .NET делается путем передачи метода do методу Invoke
( Control.Invoke
в WinForms, Dispatcher.Invoke
в WPF):
method Window1 . PredictNearFuture ; // объявлен как асинхронный в интерфейсе begin // ... Вычислить результат здесь, сохранить в переменной "theFuture" Dispatcher . Invoke ( DispatcherPriority . ApplicationIdle , method ; begin theFutureTextBox . Text := theFuture ; end ) ; end ;
Анонимные методы также могут иметь параметры:
method Window1 . PredictNearFuture ; // объявлен как асинхронный в интерфейсе begin // ... Вычислить результат здесь, сохранить в переменной "theFuture" Dispatcher . Invoke ( DispatcherPriority . ApplicationIdle , method ( aFuture : String ) ; begin theFutureTextBox . Text := aFuture ; end , theFuture ) ; end ;
Оба исходных кода используют анонимные делегаты.
Уведомление о свойствах используется в основном для связывания данных, когда GUI должен знать, когда изменяется значение свойства. Для этой цели .NET Framework предоставляет интерфейсы INotifyPropertyChanged
и INotifyPropertyChanging
(в .NET 3.5). Эти интерфейсы определяют события, которые должны быть вызваны, когда свойство изменяется/было изменено.
Oxygene предоставляет notify
модификатор, который может быть использован для свойств. Если этот модификатор используется, компилятор добавит интерфейсы в класс, реализует их и создаст код для вызова событий при изменении/изменении свойства.
свойство Foo : String чтение fFoo запись SetFoo ; уведомление ; свойство Bar : String ; уведомление 'Blubb' ; // сообщит, что свойство "Blubb" было изменено вместо "Bar"
Модификатор может использоваться для свойств, которые имеют метод setter. Код для вызова событий будет затем добавлен к этому методу во время компиляции.
пространство имен HelloWorld ; интерфейстип HelloClass = класс публичный метод класса Main ; конец ; выполнениеметод класса HelloClass.Main ; begin writeLn ( 'Hello World ! ' ) ; end ; конец .
пространство имен GenericContainer ; интерфейстип TestApp = класс публичный метод класса Main ; конец ; Человек = класс публичное свойство Имя : Строка ; свойство Фамилия : Строка ; конец ; выполнениеиспользует System.Collections.Generic ; class method TestApp.Main ; begin var myList := new List < Person >; //вывод типа myList.Add ( new Person ( FirstName := ' John' , LastName : = 'Doe' ) ) ; myList.Add ( new Person ( FirstName := ' Jane ', LastName : = ' Doe ' )) ; myList.Add ( new Person ( FirstName := ' James' , LastName : = ' Doe ' )) ; Console.WriteLine ( myList [ 1 ] .FirstName ) ; // Приведение типов не требуется Console.ReadLine ; end ; конец .
пространство имен GenericMethodTest ; интерфейстип GenericMethodTest = статический класс открытый метод класса Main ; закрытый метод класса Swap <T> ( var left , right : T ) ; метод класса DoSwap <T> ( left , right : T ) ; конец ; выполнениеclass method GenericMethodTest.DoSwap <T> ( left , right : T ) ; begin var a : = left ; var b : = right ; Console.WriteLine ( 'Type: {0}' , typeof ( T ) ) ; Console.WriteLine ( '->a = {0} , b = {1}' , a , b ) ; Swap <T> ( var a , var b ) ; Console.WriteLine ( ' - > a = {0} , b = {1}' , a , b ) ) ; end ; class method GenericMethodTest . Main ; begin var a := 23 ; // вывод типа var b := 15 ; DoSwap < Integer > ( a , b ) ; // в этом методе нет приведения к Object. var aa := 'abc' ; // вывод типа var bb := 'def' ; DoSwap < String > ( aa , bb ) ; // в этом методе нет приведения к Object. DoSwap ( 1.1 , 1.2 ) ; // вывод типа для универсальных параметров Console.ReadLine ( ) ; end ; метод класса GenericMethodTest.Swap <T> ( var left , right : T ) ; begin var temp : = left ; left : = right ; right : = temp ; end ; конец .
Вывод программы:
Тип: System.Int32-> а = 23, б = 15-> а = 15, б = 23Тип: Системная.Строка-> а = абв, б = опред.-> а = определение, б = абвТип: Система.Двойная-> а = 1,1, б = 1,2-> а = 1,2, б = 1,1
unit
: Заменено ключевым словом namespace . Поскольку Oxygene компилирует не пофайлово, а попроектно, он не зависит от имени файла. Вместо этого ключевое слово unit или namespace используется для обозначения пространства имен по умолчанию, в котором определены все типы для этого файлаprocedure
и function
: method
является предпочтительным ключевым словом, хотя procedure
и function
все еще работает.overload
: В Oxygene все методы перегружены по умолчанию, поэтому для этого не требуется специального ключевого слова..Create()
: Этот вызов конструктора был заменен ключевым new
словом. Он все еще может быть включен в project options
для устаревших причинstring
: Символы в строках отсчитываются от нуля и доступны только для чтения. Строки могут иметь нулевые значения, поэтому проверка на пустой строке не всегда достаточна.Некоторые люди [ кто? ] хотели бы перенести свой код Win32 Delphi в Oxygene без внесения существенных изменений. Это невозможно, поскольку, хотя Oxygene выглядит как Delphi, в нем достаточно изменений, чтобы сделать его несовместимым для простой перекомпиляции. Хотя название и придает ему вид другой версии Delphi, это не совсем так. [7]
Помимо разницы в языках, в Oxygene отсутствует фреймворк Visual Component Library . [8] Это еще больше затрудняет портирование, поскольку классический код Delphi в значительной степени опирается на VCL.