Oxygene (antes conocido como Chrome ) es un lenguaje de programación desarrollado por RemObjects Software para Common Language Infrastructure de Microsoft , la plataforma Java y Cocoa . Oxygene se basa en Object Pascal de Delphi , pero también tiene influencias de C# , Eiffel , Java , F# y otros lenguajes.
En comparación con el ahora obsoleto Delphi.NET , Oxygene no enfatiza la compatibilidad total con versiones anteriores, sino que está diseñado para ser una "reinvención" del lenguaje, ser un buen ciudadano en las plataformas de desarrollo administradas y aprovechar todas las características y tecnologías proporcionadas por los entornos de ejecución .NET y Java.
Oxygene es un producto comercial y ofrece una integración completa con el IDE Visual Studio de Microsoft en Windows, así como con su propio IDE llamado Fire para su uso en macOS . Oxygene es uno de los seis lenguajes compatibles con la cadena de herramientas subyacente Elements Compiler, junto con C# , Swift , Java , Go y Mercury (basado en Visual Basic.NET ).
De 2008 a 2012, RemObjects Software licenció su compilador y tecnología IDE a Embarcadero para que se usaran en su producto Embarcadero Prism . [2] A partir del otoño de 2011, Oxygene estuvo disponible en dos ediciones independientes, y la segunda edición agregó compatibilidad con los entornos de ejecución de Java y Android. A partir del lanzamiento de XE4, Embarcadero Prism ya no forma parte de la SKU de RAD Studio. Existen numerosas rutas de soporte y actualización para que los clientes de Prism migren a Oxygene. [3] A partir de 2016, solo hay una edición de Oxygene, que permite el desarrollo en Windows o macOS, y que puede crear ejecutables para Windows, Linux, WebAssembly .NET, iOS, Android, Java y macOS.
El lenguaje Oxygene tiene su origen en Object Pascal en general y Delphi en particular, pero fue diseñado para reflejar las pautas de programación .NET y para crear conjuntos totalmente compatibles con CLR. Por lo tanto, se han eliminado o revisado algunas características menores del lenguaje conocidas de Object Pascal/Delphi, mientras que se han agregado al lenguaje una serie de características nuevas y más modernas, como genéricos o secuencias y consultas.
Oxygene es un lenguaje orientado a objetos , lo que significa que utiliza clases, que pueden contener datos y ejecutar código, para diseñar programas. [ Aclaración necesaria ] Las clases son "prototipos" de objetos, como la idea de una manzana es el prototipo de la manzana que uno puede comprar en una tienda. Se sabe que una manzana tiene un color y que se puede pelar: esos son los datos y el "código" ejecutable de la clase manzana.
Oxygene proporciona soporte a nivel de lenguaje para algunas características de la programación paralela. El objetivo es utilizar todos los núcleos o procesadores de una computadora para mejorar el rendimiento. Para alcanzar este objetivo, las tareas deben distribuirse entre varios subprocesos. La clase de .NET FrameworkThreadPool
ofrecía una forma de trabajar de manera eficiente con varios subprocesos. La biblioteca de tareas paralelas (TPL) se introdujo en .NET 4.0 para proporcionar más funciones para la programación paralela.
Los operadores se pueden sobrecargar en Oxygene utilizando la class operator
sintaxis:
operador de clase implícito ( i : Entero ) : MiClase ;
Tenga en cuenta que, para la sobrecarga del operador, cada operador tiene un nombre que debe usarse en la sintaxis de sobrecarga del operador, porque, por ejemplo, "+" no sería un nombre de método válido en Oxygene. [4]
Oxygene no utiliza "Unidades" como Delphi, sino que utiliza espacios de nombres .NET para organizar y agrupar tipos. Un espacio de nombres puede abarcar varios archivos (y ensambles), pero un archivo solo puede contener tipos de un espacio de nombres. Este espacio de nombres se define en la parte superior del archivo:
espacio de nombres ConsoleApplication1;
Los archivos Oxygene se dividen en una interfaz y una sección de implementación, que es la estructura conocida de Delphi. La sección de interfaz sigue a la declaración del espacio de nombres. Contiene la uses
cláusula que en Oxygene importa tipos de otros espacios de nombres:
utiliza Sistema . Linq ;
Los espacios de nombres importados deben estar en el propio proyecto o en los ensambles a los que se hace referencia. A diferencia de C#, en Oxygene no se pueden definir nombres de alias para espacios de nombres, solo para nombres de un solo tipo (consulte a continuación).
A continuación de la uses
cláusula, un archivo contiene declaraciones de tipo, tal como se conocen en Delphi:
interfaztipo ConsoleApp = clase clase pública método Main ; fin ;
Al igual que en C#, el método Main es el punto de entrada de cada programa. Puede tener un parámetro args : Array of String
para pasar argumentos de línea de comandos al programa.
Se pueden declarar más tipos sin repetir la type
palabra clave.
La implementación de los métodos declarados se coloca en la sección de implementación:
implementaciónmétodo de clase ConsoleApp . Main ; begin // agrega tu propio código aquí Console . WriteLine ( 'Hola mundo.' ) ; end ; fin .
Los archivos siempre terminan conend.
Como lenguaje .NET, Oxygene utiliza el sistema de tipos .NET: hay tipos de valor (como estructuras) y tipos de referencia (como matrices o clases).
Aunque no introduce tipos "predefinidos" propios, Oxygene ofrece nombres genéricos más "pascalianos" para algunos de ellos, [5] de modo que, por ejemplo, se System.Int32
pueden utilizar como Integer
y Boolean
( System.Boolean
), Char
( System.Char
), Real
( System.Double
) y también se unen a la familia de nombres de tipos pascal. El carácter de estructura de estos tipos, que es parte de .NET, se conserva por completo.
Como en todos los lenguajes .NET, los tipos en Oxygene tienen una visibilidad. En Oxygene, la visibilidad predeterminada es assembly
, que es equivalente a la internal
visibilidad en C#. La otra visibilidad de tipo posible es public
.
tipo MiClase = clase pública fin ;
La visibilidad se puede configurar para cada tipo definido (clases, interfaces, registros, ...).
Se puede definir un nombre de alias para los tipos, que se pueden usar localmente o en otros ensamblajes de Oxygene.
tipo IntList = public List < Integer >; //visible en otros ensamblados Oxygene SecretEnumerable = IEnumerable < String >; //no visible en otros ensamblados
Los alias de tipo públicos no serán visibles para otros idiomas.
Los registros son el nombre que reciben las estructuras .NET en Oxygene. Se declaran igual que las clases, pero con la record
palabra clave:
tipo MyRecord = método de registro Foo ; fin ;
Como son solo estructuras .NET, los registros pueden tener campos, métodos y propiedades, pero no tienen herencia y no pueden implementar interfaces .
Las interfaces son un concepto muy importante en el mundo .NET, el propio framework hace un uso intensivo de ellas. Las interfaces son la especificación de un pequeño conjunto de métodos, propiedades y eventos que una clase debe implementar al implementar la interfaz. Por ejemplo, la interfaz IEnumerable<T>
especifica el GetEnumerator
método que se utiliza para iterar sobre secuencias.
Las interfaces se declaran igual que las clases:
tipo MyInterface = interfaz pública método MakeItSo : IEnumerable ; propiedad Bar : String lectura escritura ; fin ;
Tenga en cuenta que, para las propiedades, el captador y el definidor no se especifican explícitamente.
Los delegados definen firmas para los métodos, de modo que estos métodos se puedan pasar en parámetros (por ejemplo, devoluciones de llamadas) o almacenar en variables, etc. Son el equivalente de NET con seguridad de tipos a los punteros de función. También se utilizan en eventos. Al asignar un método a un delegado, uno tiene que usar el @
operador, de modo que el compilador sepa que uno no quiere llamar al método, sino simplemente asignarlo.
Oxygene puede crear delegados anónimos; por ejemplo, se pueden pasar métodos al Invoke
método de un control sin declarar el delegado:
método MainForm . MainForm_Load ( remitente : System . Object ; e : System . EventArgs ) ; inicio Invoke ( @ DoSomething ) ; fin ;
DoSomething
El compilador creará un delegado anónimo con la firma del método .
Oxygene admite delegados polimórficos, lo que significa que los delegados que tienen parámetros de tipos descendentes son compatibles con la asignación. Supongamos que hay dos clases MyClass
y MyClassEx = class(MyClass)
, entonces en el siguiente código BlubbEx
es compatible con la asignación Blubb
.
tipo delegado Blubb ( remitente : Objeto ; m : MiClase ) ; delegado BlubbEx ( remitente : Objeto ; mx : MiClaseEx ) ;
Los campos se pueden usar para delegar la implementación de una interfaz, si el tipo del que son implementa esta interfaz:
Implementador = clase pública ( IMyInterface ) // ... implementar interfaz ... fin ; MyClass = public class ( IMyInterface ) fSomeImplementor : Implementor ; public implements IMyInterface ; //se encarga de implementar la interfaz end ;
En este ejemplo, el compilador creará métodos y propiedades públicos en MyClass
, que invocarán los métodos/propiedades de fSomeImplementor
, para implementar los miembros de IMyInterface. Esto se puede utilizar para proporcionar una funcionalidad similar a la de un mixin. [6]
Los métodos anónimos se implementan dentro de otros métodos. No se puede acceder a ellos fuera del método a menos que se almacenen dentro de un campo delegado. Los métodos anónimos pueden usar las variables locales del método en el que se implementan y los campos de la clase a la que pertenecen.
Los métodos anónimos son especialmente útiles cuando se trabaja con código que se supone que debe ejecutarse en un hilo de GUI, lo que se hace en .NET pasando un método al Invoke
método ( Control.Invoke
en WinForms, Dispatcher.Invoke
en WPF):
método Window1 . PredictNearFuture ; //declarado como asíncrono en la interfaz begin // ... Calcular el resultado aquí, almacenar en la variable "theFuture" Dispatcher . Invoke ( DispatcherPriority . ApplicationIdle , método ; begin theFutureTextBox . Text := theFuture ; end ) ; end ;
Los métodos anónimos también pueden tener parámetros:
método Window1 . PredictNearFuture ; //declarado como async en la interfaz begin // ... Calcular el resultado aquí, almacenar en la variable "theFuture" Dispatcher . Invoke ( DispatcherPriority . ApplicationIdle , método ( aFuture : String ) ; begin theFutureTextBox . Text := aFuture ; end , theFuture ) ; end ;
Ambos códigos fuente utilizan delegados anónimos.
La notificación de propiedades se utiliza principalmente para la vinculación de datos, cuando la GUI debe saber cuándo cambia el valor de una propiedad. El marco .NET proporciona las interfaces INotifyPropertyChanged
y INotifyPropertyChanging
(en .NET 3.5) para este propósito. Estas interfaces definen eventos que deben activarse cuando se cambia o se modificó una propiedad.
Oxygene proporciona el notify
modificador que se puede utilizar en las propiedades. Si se utiliza este modificador, el compilador agregará las interfaces a la clase, las implementará y creará código para generar los eventos cuando la propiedad cambie o haya cambiado.
propiedad Foo : String leer fFoo escribir SetFoo ; notificar ; propiedad Bar : String ; notificar 'Blubb' ; //notificará que se cambió la propiedad "Blubb" en lugar de "Bar"
El modificador se puede utilizar en propiedades que tienen un método de establecimiento. El código para generar los eventos se agregará a este método durante el tiempo de compilación.
espacio de nombres HelloWorld ; interfaztipo HelloClass = clase clase pública método Main ; fin ; implementaciónmétodo de clase HelloClass . Main ; begin writeLn ( '¡Hola mundo!' ) ; end ; fin .
espacio de nombres GenericContainer ; interfaztipo TestApp = clase clase pública método Main ; fin ; Persona = clase propiedad pública Nombre : String ; propiedad Apellido : String ; fin ; implementaciónutiliza Sistema . Colecciones . Genérico ; método de clase TestApp . Main ; begin var myList := new List < Person >; //inferencia de tipo 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 ) ; //No se necesita conversión Console . ReadLine ; end ; fin .
espacio de nombres GenericMethodTest ; interfaztipo GenericMethodTest = clase estática clase pública método Main ; clase privada método Swap < T > ( var left , right : T ) ; clase método DoSwap < T > ( left , right : T ) ; fin ; implementaciónclase método GenericMethodTest . DoSwap < T > ( izquierda , derecha : T ) ; comienzo var a := izquierda ; var b := derecha ; Console . WriteLine ( 'Tipo: {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 ) ; fin ; método de clase GenericMethodTest . Main ; begin var a := 23 ; // inferencia de tipo var b := 15 ; DoSwap < Integer > ( a , b ) ; // no hay conversión descendente a Object en este método. var aa := 'abc' ; // inferencia de tipo var bb := 'def' ; DoSwap < String > ( aa , bb ) ; // no hay conversión descendente a Object en este método. DoSwap ( 1.1 , 1.2 ) ; // inferencia de tipos para parámetros genéricos Console.ReadLine () ; fin ; método de clase GenericMethodTest . Swap < T > ( var izquierda , derecha : T ) ; comienzo var temp := izquierda ; izquierda := derecha ; derecha := temp ; fin ; fin .
Salida del programa:
Tipo: Sistema.Int32-> a = 23, b = 15-> a = 15, b = 23Tipo: System.String-> a = abc, b = definición-> a = definición, b = abcTipo: Sistema.Doble-> a = 1,1, b = 1,2-> a = 1,2, b = 1,1
unit
: Se reemplaza con la palabra clave namespace . Dado que Oxygene no compila por archivo sino por proyecto, no depende del nombre del archivo. En su lugar, se utiliza la palabra clave unit o namespace para indicar el espacio de nombres predeterminado en el que se definen todos los tipos para ese archivo.procedure
y function
: method
es la palabra clave preferida, aunque procedure
todavía function
funciona.overload
:En Oxygene todos los métodos están sobrecargados de forma predeterminada, por lo que no se necesita ninguna palabra clave especial para esto..Create()
:Esta llamada al constructor ha sido reemplazada por la new
palabra clave. Aún se puede habilitar por project options
razones heredadas.string
:Los caracteres de las cadenas se basan en cero y son de solo lectura. Las cadenas pueden tener valores nulos, por lo que no siempre es suficiente realizar pruebas con cadenas vacías.A algunas personas [¿ quiénes? ] les gustaría portar su código Win32 de Delphi a Oxygene sin realizar cambios importantes. Esto no es posible porque, si bien Oxygene se parece a Delphi, hay suficientes cambios como para que sea incompatible con una simple recompilación. Si bien el nombre le da la apariencia de otra versión de Delphi, eso no es completamente cierto. [7]
Además de la diferencia de lenguaje, el marco de la Biblioteca de componentes visuales no está disponible en Oxygene. [8] Esto hace que la portabilidad sea aún más difícil porque el código clásico de Delphi depende en gran medida de VCL.