Oxygene (programming language)

Oxygene (programming language)
Oxygene
Chrome-128.png
Developer RemObjects Software
Stable release 3.0.21 (August 29, 2009; 2 years ago (2009-08-29))
Influenced by Object Pascal, C#
Platform Common Language Infrastructure
License Commercial
Website remobjects.com/oxygene

Oxygene (formerly known as Chrome) is a programming language developed by RemObjects Software for Microsoft's Common Language Infrastructure. Oxygene is Object Pascal-based. Compared to the now deprecated Delphi.NET, Oxygene does not emphasize total backward compatibility, but is designed to be a full .NET citizen and leverage all the features and technologies provided by the .NET runtime.

Starting 2008, RemObjects Software has licensed its compiler and IDE technology to Embarcadero to be used in their Delphi Prism product.[1] Delphi Prism offers full integration into Visual Studio 2005/2008/2010. Starting the Fall of 2011, Oxygene is adds support for the Java and Android runtimes, as well. The Embarcadero Prism product continues to focus on .NET, with Java support being available as a separate product from RemObjects Software. Both products/platforms share the same core compiler and development environment.

Contents

Oxygene language

The Oxygene language has its origins in Object Pascal in general and Delphi in particular, but was designed to reflect the guidelines of .NET programming and to create fully CLR-compliant assemblies. Therefore not all language features known from Object Pascal / Delphi can be found any longer in Oxygene or only as legacy features.

Object-oriented programming

Oxygene is an object-oriented language, which means it uses classes, which can hold data and execute code, to design programs. Classes are "prototypes" for objects, like the idea of an apple is the prototype for the apple you can actually buy in the shop. You know that an apple has a color, and you know that it can be peeled: those are the data and executable "code" for the apple class.

Parallel programming

Oxygene provides language-level support for some of features of parallel programming. The goal is to use all cores or processors of a computer to improve performance. To reach this goal, tasks have to be distributed among several threads. The .NET framework's ThreadPool class offered a way to efficiently work with several threads. The Task Parallel Library (TPL) was introduced in .NET 4.0 to provide more features for parallel programming.

Operator overloading

Operators can be overloaded in Oxygene using the class operator syntax:

class operator implicit(i : Integer) : MyClass;

Note, that for operator overloading each operator has a name, that has to be used in the operator overloading syntax, because for example "+" would not be a valid method name in Oxygene[2].

Program structure

Oxygene does not use "Units" like Delphi does, but uses .NET-namespaces to organize and group types. A namespace can span multiple files (and assemblies), but one file can only contain types of one namespace. This namespace is defined at the very top of the file:

namespace ConsoleApplication1;

Oxygene files are separated into an interface and an implementation section, which is the structure known from Delphi. The interface section follows the declaration of the namespace. It contains the uses-clause, which in Oxygene imports types from other namespaces:

uses 
  System.Linq;

Imported namespaces have to be in the project itself or in referenced assemblies. Unlike in C#, in Oxygene you cannot define alias names for namespaces, only for single type names (see below).

Following the uses-clause a file contains type declarations, like they are known from Delphi:

interface
 
type
  ConsoleApp = class
  public
    class method Main;
  end;

As in C#, the Main-method is the entry point for every program. It can have a parameter args : Array of String for passing command line arguments to the program.

More types can be declared without repeating the type-keyword.

The implementation of the declared methods is places in the implementation section:

implementation
 
class method ConsoleApp.Main;
begin
  // add your own code here
  Console.WriteLine('Hello World.');
end;
 
end.

Files are always ended with end.

Examples of Oxygene .NET

Types

As a .NET language, Oxygene uses the .NET type system: There are value types (like structs) and reference types (like arrays or classes).

Although it does not introduce own "pre-defined" types, Oxygene offers more "pascalish" generic names for some of them[3], so that for example the System.Int32 can be used as Integer and Boolean (System.Boolean), Char (System.Char), Real (System.Double) join the family of pascal-typenames, too. The struct character of these types, which is part of .NET, is fully preserved.

Type visibility

As in all .NET languages types in Oxygene have a visibility. In Oxygene the default visibility is assembly, which is equivalent to the internal visibility in C#. The other possible type visibility is public.

type
  MyClass = public class
  end;

The visibility can be set for every type you define (classes, interfaces, records, ...).

Type aliases

You can define an alias name for types, which can be used locally or in other Oxygene-assemblies, too.

type
  IntList = public List<Integer>; //visible in other Oxygene-assemblies
  SecretEnumerable = IEnumerable<String>; //not visible in other assemblies

Public type aliases won't be visible for other languages.

Value types
Records

Records are what .NET-structs are called in Oxygene. They are declared just like classes, but with the record keyword:

type
  MyRecord = record
    method Foo;
  end;

As they're just .NET-structs, records can have fields, methods and properties, but do not have inheritance and cannot implement interfaces.

Interfaces

Interfaces are very important concept in the .NET world, the framework itself makes heavy use of them. Interfaces are the specification of a small set of methods, properties and events a class has to implement when implementing the interface. For example contains the interface IEnumerable<T> specifies the GetEnumerator method which is used to iterate over sequences.

Interfaces are declared just like classes:

type
  MyInterface = public interface
    method MakeItSo : IEnumerable;
    property Bar : String read write;
  end;

Please notice, that for properties the getter and setter are not explicitly specified.

Delegates

Delegates define signatures for methods, so that these methods can be passed in parameters (e.g. callbacks) or stored in variables, etc. They're the type-safe NET-equivalent to function pointers. They're also used in events. When assigning a method to a delegate, one has to use the @ operator, so the compiler knows, that one doesn't want to call the method but just assign it.

Oxygene can create anonymous delegates, so for example you can pass methods to the Invoke method of a control without declaring the delegate:

method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs);
begin
  Invoke(@DoSomething);
end;

An anonymous delegate with the signature of the method DoSomething will be created by the compiler.

Oxygene supports polymorphic delegates, which means, that delegates which have parameters of descending types are assignment compatible. Assume two classes MyClass and MyClassEx = class(MyClass), then in the following code BlubbEx is assignment compatible to Blubb.

type
  delegate Blubb(sender : Object; m : MyClass);
  delegate BlubbEx(sender : Object; mx : MyClassEx);
Delegating interface implementation

Fields can be used to delegate the implementation of an interface, if the type they're of implements this interface:

Implementor = public class(IMyInterface)
  // ... implement interface ...
end;
 
MyClass = public class(IMyInterface)
  fSomeImplementor : Implementor; public implements IMyInterface; //takes care of implementing the interface
end;

In this example the compiler will create public methods and properties in MyClass, which call the methods / properties of fSomeImplementor, to implement the members of IMyInterface. This can be used to provide mixin-like functionality[4].

Anonymous methods

Anonymous methods are implemented inside other methods. They are not accessible outside of the method unless stored inside a delegate field. Anonymous methods can use the local variables of the method they're implemented in and the fields of the class they belong to.

Anonymous methods are especially useful when working with code that is supposed to be executed in a GUI thread, which is done in .NET by passing a method do the Invoke method (Control.Invoke in WinForms, Dispatcher.Invoke in WPF):

method Window1.PredictNearFuture;  //declared as async in the interface
begin
  // ... Calculate result here, store in variable "theFuture"
    Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, method; begin
      theFutureTextBox.Text := theFuture;
    end);
end;

Anonymous methods can have parameters, too:

method Window1.PredictNearFuture;  //declared as async in the interface
begin
  // ... Calculate result here, store in variable "theFuture"
    Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, method(aFuture : String); begin
      theFutureTextBox.Text := aFuture ;
    end, theFuture);
end;

Both source codes use anonymous delegates.

Property notification

Property notification is used mainly for data binding, when the GUI has to know when the value of a property changes. The .NET framework provides the interfaces INotifyPropertyChanged and INotifyPropertyChanging (in .NET 3.5) for this purpose. These interfaces define events which have to be fired when a property is changed / was changed.

Oxygene provides the notify modifier, which can be used on properties. If this modifier is used, the compiler will add the interfaces to the class, implement them and create code to raise the events when the property changes / was changed.

property Foo : String read fFoo write SetFoo; notify;
property Bar : String; notify 'Blubb'; //will notify that property "Blubb" was changed instead of "Bar"

As you can see, the modifier can be used on properties which have a setter method. The code to raise the events will then be added to this method during compile time.

Oxygene language syntax

Literals

String literals

String literals can be written with single- or double-quotes. Single-quoted strings have to contain more than one character, otherwise they're treated as character literals. Double-quoted strings can span multiple lines, the line breaks will be part of the string value.

var a := 'a'; //char
var b := "a"; //string
var c := 'ab'; //string
var d := "line
break";

Character literals

Character literals can be written as single-quoted letters, or using their character number prepended by a "#".

Integer literals

Integer literals can be written as "normal" decimal numbers, as hexadecimal numbers (prependend with a "$") or as binary numbers (prepended with a "%").

//All variables have the same value:
var a := 16;
var b := $10;
var c := %10000;

Floating point literals

By default every floating point literal that has no other type information connected to it, is treated as double. Floating point literals are written as digits, followed by the period as decimal separator and then another sequence of digits. A syntax for exponential write is supported: 31.415926E-1

Identifiers

It must not start with a digit, but may contain them. The underscore "_" is allowed at the beginning and inside an identifier. Keywords can be used as identifiers, if they are escaped with an ampersand "&". A "Special Identifier Syntax" can be used, to use any character in an identifier-name[5].

Identifiers in Oxygene are case-insensitive.

Keywords

Oxygene has global keywords and conditional keywords, that only act as keywords in a special context.

Oxygene Global Keywords
and as assembly begin break
case class const constructor continue
delegate div do downto else
end ensure event except exit
false finalizer finally for forward1
function1 future if implementation in
inherited interface invariants is locking
loop method mod module namespace
new nil not nullable of
old on operator or out
parallel private procedure1 property protected
public raise record repeat require
result self set shl shr
then to true try type
unit until uses using var
where while with xor yield
1These keywords are there for legacy only.
Oxygene Conditional Keywords
abstract add array of async default
each empty enum external final
finalizer flags global has implements
implies index inline iterator locked
matching nested notify override params
partial pinned read readonly reintroduce
remove sealed sequence of static step
unsafe virtual where write
The following words are only keywords in query expressions:
asc desc distinct equals from
group by into join on order by
reverse select skip take

Program structure

Oxygene does not use "Units" like Delphi does, but uses .NET-namespaces to organize and group types. A namespace can span multiple files (and assemblies), but one file can only contain types of one namespace. This namespace is defined at the very top of the file:

namespace ConsoleApplication1;

Oxygene files are separated into an interface and an implementation section, which is the structure known from Delphi. The interface section follows the declaration of the namespace. It contains the uses-clause, which in Oxygene imports types from other namespaces:

uses 
  System.Linq;

Imported namespaces have to be in the project itself or in referenced assemblies. Unlike in C#, in Oxygene you cannot define alias names for namespaces, only for single type names (see below).

Following the uses-clause a file contains type declarations, like they are known from Delphi:

interface
 
type
  ConsoleApp = class
  public
    class method Main;
  end;

As in C#, the Main-method is the entry point for every program. It can have a parameter args : Array of String for passing command line arguments to the program.

More types can be declared without repeating the type-keyword.

The implementation of the declared methods is places in the implementation section:

implementation
 
class method ConsoleApp.Main;
begin
  // add your own code here
  Console.WriteLine('Hello World.');
end;
 
end.

Files are always ended with end.

Variables

Variables in Oxygene can be declared in the head of a method or inside its body:

method MyClass.Foo;
var
  var1 : Integer; //Variable declared in header
begin
  var1 := 5; //Assignment to variable
 
  var var2 : Integer; //Variable declared in the body
  var var3 : Integer := 10; //Variable declared and initialized in the body
  var var4 := 'foobar'; //String-Variable declared using type inference
end;

As seen in the above example, in the method one can use type inference to declare and initialize a variable without explicitly defining its type.

Constants

Constants can be defined as class members in the interface section or in the header of every method. Their value is defined via "=", not ":=", because it's a definition not an assignment. Type inference works with constants, too.

const pi = 3.1415926;

Their value can only be defined during declaration and will be evaluated at compile time.

Operators

Boolean and binary operators

As other Pascal languages, Oxygene does not distinguish between a boolean and and a binary and. The following operators are both boolean and binary: not, and, or, xor.

Additionally, there's the only-boolean implies-Operator. It combines two boolean values. The second expression is only evaluated if the first/left expression is true. If the first expression is false, the entire expression will be considered true.

ensure
  HasDriversLicense implies Age >= 16;

In combination with is and in the not operator can be used in a more human readable way:

if i not in [1,2,3] then
  //...
if i is not String then
  //...

Comparison operators

Oxygene does have the following comparison operators: >=, <=, <>, =, >, <.

In Oxygene you can use the "Boolean Double Comparison":

if -2 <= a <= 2 then
//...

is equal to

if (-2 <= a) and (a <= 2) then
//...

.

Assignment operators

To assign values in Oxygene, you normally use the := operator. For adding and removing event handlers (and only for that), Oxygene does know the "addition assignment operator" += and the "subtraction assignment operator" -=.

Arithmetic operators

The operators for addition, subtraction, division and multiplication are +, -, /, *. Although the division-operator used with two integers creates in Oxygene already an integer division, it has still the "old" div operator (integer division) known from other Pascal languages. The modulo of two integers is calculated using the mod operator.

Member access operators

Oxygene offers two operators for accessing the members of an object. The period operator "." works as expected and will raise an exception when used on a nil-value.

Different from the period operator, which requires the value on its left to be assigned (and will usually throw a NullReferenceException if it is not), the colon operator allows calls to members on any value, including nil. If the expression on the left of the colon is nil, the entire expression will automatically short-circuit to return nil as well.

var lGreatGrandDadsName := lUser:Parent:Parent:Parent:Name;

Bit shifting operators

shl will shift bits to the left, shr will shift bits to the right.

Other operators

  • @ is used to get the address of something, which for example is used when passing a method as parameter.*
  • [] is used to access array elements.
  • -> is used for lambda expressions.
  • as is a casting operator, throwing an exception if the cast is unsuccessful.
  • is checks if an object is assignable to a variable of specific type.
  • in checks if an element is in a collection of other elements, also used for Set-types.
  • ^ is the pointer dereferencing operator.
  • iif is the equivalent to the conditional operator in C languages: var a := iif(b = 42, 3, 4); //3, if b = 42, 4 else

Operator overloading

Operators can be overloaded in Oxygene using the class operator syntax:

class operator implicit(i : Integer) : MyClass;

Note, that for operator overloading each operator has a name, that has to be used in the operator overloading syntax, because for example "+" would not be a valid method name in Oxygene[6].

Control structures

Blocks of code

Blocks of code are created by surrounding them with begin and end. Variables declared in a code block are not visible / usable outside of it.

Conditional structures

if statement

A complete if statement in Oxygene consists of the

if {condition} then
  {code}
else
  {code}

The code can be one command or a block of code. When nesting if statements without creating separate code blocks, else branches always belong to the next inner if branch, which is indicated by indentation in the following code:

 if {condition} then
   if {condition} then
     {command}
   else
     {command};
case statement

The case statement executes code, depending on the value of a variable:

case i of
  1,2,3: {code}
  4..9: {code}
  10: begin
        {code}
      end;
else
  {code}
end;

Unlike Delphi, Oxygene also accepts types that are not ordinal such as strings.

The case statement can also be used to execute code depending on the type of a variable:

case foo type of
  Integer: Console.WriteLine(foo.ToString);
  Double: Console.WriteLine(foo.ToString('0.00'));
  Boolean: Console.WriteLine(iif(Boolean(foo), 'yes', 'no'));
else
  Console.WriteLine('Hu? What''s that?');
end;

Loops

for Loop

In Oxygene, you can (but don't need to) declare the iteration variable inside of the loop's head and its scope will be limited to the loop's body:

for i : Integer := 0 to 42 do
  Console.WriteLine(i.ToString);

This even compiles, if there's already a variable with the same name declared. This "outside variable" then can not be accessed from within the loop's body.

for loops can run backward (using downto instead of to) and make steps larger than one (0 to 42 step 4).

repeat-until Loop

The repeat-until loop is a post-test loop, which means its body is executed at least one time before the loop-condition is checked:

var i := 0;
repeat
  Console.WriteLine("foo");
  inc(i);
until i = 10;
while-do Loop

The while-do is a pre-test loop, the condition is checked before the body is (perhaps) executed the first time.

var i := 0;
while i < 10 do
begin
  Console.WriteLine('foo');
  inc(i);
end;
loop Loop

The loop loop executes its body without any condition indefinitely, until it is aborted using break (see below).

loop Console.WriteLine('foo'); //will run forever!
var i := 0;
loop begin
  Console.WriteLine('foo');
  inc(i);
  if i = 10 then
    break;
end;
for each (matching) Loop

With the for each loop one can iterate over a sequence, i.e. anything that implements IEnumerable and/or IEnumerable<T>. The "each" can be omitted.

for i in Enumerable.Range(1,10) do
  Console.WriteLine(i);

As can be seen in the above example, the type of the iterator variable is inferred for typed sequences (i.e. a sequence implementing IEnumerable<T>).

Only items of a specific type can be filtered with the matching syntax:

for matching item : IComparable in items do
  //...

This will execute the loop body only for items implementing IComparable and "jumping over" other items in the sequence.

The (zero-based) index of the current iteration can be obtained with the index syntax:

for item in items index i do
  Console.WriteLine((i+1).ToString+'. Item: '+item.ToString);
Controlling Loops

Loops can be controlled by using the break or continue keyword. The former aborts the loop completely, the latter aborts the current iteration and starts the next one. Both keywords only affect the most inner loop.

Exception Handling

Oxygene has the try-except and try-finally statements for handling exception, which can be combined and used together.

try
  //Do something very, very dangerous
except
  //React if it goes wrong
finally
  //Clean-up
end;

The finally block will be executed regardless of an exception is thrown or not, and if only the finally block and no except block is there, the exception will "bubble up" in your program, while in an except block you have to use the raise; command to keep re-raise an already handled exception.

You can select on which types of exceptions you want to react:

try
 //Do something even more dangerous
except
  on e : BioHazardException do
    Console.WriteLine('omg, we released a dangerous liquid with the name: '+e.message);
  on NuclearException do
    Console.WriteLine('Ooops, we set off a nuke!');
  on Exception do
    Console.WriteLine('Something else went wrong, no idea what');
end;

The last selector is used to catch every exception that wasn't handled before and can be seen as some kind of else-branch. You can also see, that the exception object e can be used or not.

You can filter exceptions further using the where-syntax:

try
 //You know what goes here ..
except
  on e : CategorizedException where e.CategoryId = 42 do
    Console.WriteLine('Got exception with of category 42');
end;

Types

As a .NET language, Oxygene uses the .NET type system: There're value types (like structs) and reference types (like arrays or classes).

Although it does not introduce own "pre-defined" types, Oxygene offers more "pascalish" generic names for some of them[7], so that for example the System.Int32 can be used as Integer and Boolean (System.Boolean), Char (System.Char), Real (System.Double) join the family of pascal-typenames, too. The struct character of these types, which is part of .NET, is fully preserved.

Type visibility

As in all .NET languages types in Oxygene have a visibility. In Oxygene the default visibility is assembly, which is equivalent to the internal visibility in C#. The other possible type visibility is public.

type
  MyClass = public class
  end;

The visibility can be set for every type you define (classes, interfaces, records, ...).

Type Aliases

You can define an alias name for types, which can be used locally or in other Oxygene-assemblies, too.

type
  IntList = public List<Integer>; //visible in other Oxygene-assemblies
  SecretEnumerable = IEnumerable<String>; //not visible in other assemblies

Public type aliases won't be visible for other languages.

Value types

Records

Records are what .NET-structs are called in Oxygene. They are declared just like classes, but with the record keyword:

type
  MyRecord = record
    method Foo;
  end;

As they're just .NET-structs, records can have fields, methods and properties, but do not have inheritance and cannot implement interfaces.

Enumerations

Enumerations can be defined using the enum keyword or a shorter syntax:

type
  MyEnum1 = public enum(One, Two, Thee);
  MyEnum2 = public(One, Two, Three);

By default, the integer value representing one item in the enum is increased by 1 for every item. Declaring a enum using the flags keyword will assign every item a value of 2i, where i is the index of the item. You can define the value of an item manually, too.

MyFlags = flags(One, Two, Thee);
MyEnum3 = enum(One = 1, Two = 2, Three = 3);
Sets

Sets are convenient way of working with a small, defined number of ordinal values, like the elements of an enumeration. Every value is exactly zero ore one time in a set.

type
  MySet = set of MyEnum;

You can add values to a set or check, if a value is in a set:

var s : MySet := [MySet.One];
s := s + [MySet.Two];
if MySet.Three in s then
  Console.WriteLine("Jepp!");

Other operations are subtraction, intersection (*) and checks for equality, inequality, subset and superset.

Internally sets are implemented using structs and these structs are usable from other languages like C#, too.

Nullable types

Nullable types are a way, to store whether the value of a value type is set or not. The .NET framework provides the generic Nullable<T> struct for this purpose, which is integrated into the language in Oxygene.

When declaring e.g. a nullable Integer

var nullableInt : nullable Integer;

you can just assign a "normal" integer to it:

nullableInt  := 5;

You can use any method of the value's type on the nullable type:

if nullableInt.CompareTo(3) > 0 then
  Console.WriteLine('Is bigger');

You should consider the use of the colon-operator in this case, because it's "nil-safe" and won't throw an exception if the value is not set.

Nullable types in Oxygene can be uses inside expressions[8], where using a "normal" and a nullable type in one expression will in almost every case result in a nullable type.

var k := nullableInt  + 5; //k is nullable

Nullables can be used inside boolean expressions, where the equality comparisons (= and <>) will always result in non-nullable boolean, all other comparisons will create a nullable boolean:

if nullableInt = 5 then
  Console.WriteLine('Jepp!');

To obtain the value of a nullable value, Oxygene provides the system method valueOrDefault:

var aInt := valueOrDefault(nullableInt, -1);

This will assign the value of nullableInt, if the value was set, and -1 otherwise. The last parameter can be omitted, then the default value of the non-nullable type will be used as "fallback"-value.

if valueOrDefault(nullableInt > 5) then
  Console.WriteLine("Is bigger!")

Here nullableInt > 5 creates a nullable boolean and valueOrDefault will return false, if the value is not set and its value otherwise.

Although their name indicates something else, nullable types are value types (so assigning one to another will create a copy). One can assign them the null / nil value, but only because there is an implicit conversion for this case. For the same reason, you can compare them to nil in order to find out, if their value was set:

if nullableInt = nil then
  Console.WriteLine('Value not set!');

Reference Types

When the type of a variable is reference type, then the variable only holds a reference to an instance of that type. Assigning the variable to another variable just copies the reference, so both variables reference the same instance. When there're no more reference to an instance, it will eventually get removed by the garbage collection (GC).

The null-reference (i.e. the value of a variable not referencing anything) in Oxygene is nil.

Arrays

Oxygene uses a mixed Pascal / C style for declaring and initializing arrays. The type of an array is declared in the pascal-way

var a : Array of Integer;

but the instance is created using C-style (which is used always for creating instances in Oxygene)

a := new Integer[42];

Due to type inference you could of course omit the declaration.

In addition to these unbound array, Oxygene offers the possibility to use bound arrays. Bound arrays can have both the lower and upper bound defined (then they're of fixed size) or only the lower bound (then they're of variable size).

type
  ArrType1 = array[0..3] of Integer;
  ArrType2 = array[1..] of Char;
  ArrType3 = array[-2..2] of String;
 
//...
 
var a := new ArrType2(10);

For fixed size arrays you don't need to create the instance. As you can see, the lower bound is allowed to be negative.

You can use char and enums as array indexes, too:

var
  a : Array[MyEnum] of Integer;
  b : Array[Char] of Byte;

Multidimensional arrays can be declared (or type inferred) like this:

var a := new Integer[3,4];
var b : Array[0..,0..] of Integer;

Inline array constants can be used like this:

listBox1.Items.AddRange([3,4,5]);
var x : array of Integer := [1,2,3];
var b : array[0..,0..] of Integer := [[3,3,3],[4,5,6]];
Classes

A class is a data type that can contain fields, properties, methods, indexers and events. Also nested types can be declared inside a class.

Classes can be sealed (cannot be inherited), abstract or static. Classes can be declared as partial, so that the class declaration can span multiple files (but not multiple assemblies).

Classes can inherit other classes and implement interfaces.

Example of a class delcaration in Oxygene:

type
  MyClass = public class(ParentClass, ISampleInterface) //inherits ParentClass, implement ISampleInterface
  protected
    fFoo : Integer; //field
    method GetList(index : Integer) : String;
    method SetList(index : Integer; value : String);
  public
    method DoSomething; //method
    property Foo : Integer read fFoo; //read-only property
    property List[index : Integer] : String read GetList write SetList; default; //(default) indexer
  end;
 
  SubClass nested in MyClass = protected class //Class nested in MyClass
    event SomethingHappens : MyEvent;
  end;
Interfaces

Interfaces are very important concept in the .NET world, the framework itself makes heavy use of them. Interfaces are the specification of a small set of methods, properties and events a class has to implement when implementing the interface. For example contains the interface IEnumerable<T> specifies the GetEnumerator method which is used to iterate over sequences.

Interfaces are declared just like classes:

type
  MyInterface = public interface
    method MakeItSo : IEnumerable;
    property Bar : String read write;
  end;

Please notice, that for properties the getter and setter are not explicitly specified.

Delegates

Delegates define signatures for methods, so that these methods can be passed in parameters (e.g. callbacks) or stored in variables, etc. They're the type-safe NET-equivalent to function pointers. They're also used in events.

To define a delegate one uses the method or delegate keyword. function and procedure work, too, but are deprecated.

type
  SomeCallback = public method(aParam : Integer) : Boolean;
  SomeEventHandler = public delegate(sender : Object; e : SomeEventArgs);

Delegates can be invoked by using there Invoke or BeginInvoke method, or by just calling them like methods:

var a : SomeCallback := @anAppropriateMethod;
a(42);

As you can see, when assigning a method to a delegate, one has to use the @ operator, so the compiler knows, that one doesn't want to call the method but just assign it.

Oxygene can create anonymous delegates, so for example you can pass methods to the Invoke method of a control without declaring the delegate:

method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs);
begin
  Invoke(@DoSomething);
end;

An anonymous delegate with the siganture of the method DoSomething will be created by the compiler.

This can be combined with anonymous methods:

method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs);
begin
  Invoke(method; begin
    MessageBox.Show('foo');
  end);
end;

Oxygene supports polymorphic delegates, which means, that delegates which have parameters of descending types are assignment compatible. Assume two classes MyClass and MyClassEx = class(MyClass), then in the following code BlubbEx is assignment compatible to Blubb.

type
  delegate Blubb(sender : Object; m : MyClass);
  delegate BlubbEx(sender : Object; mx : MyClassEx);

Object-oriented programming

Oxygene is an object-oriented language, which means it uses classes, which can hold data and execute code, to design programs. Classes are "prototypes" for objects, like the idea of an apple is the prototype for the apple you can actually buy in the shop. You know, that an apple has a color and you know, that it can be peeled: that are is data and executable "code" for the apple class.

Methods

Methods are declared in the interface section and implemented in the implementation section of a file:

interface
 
type
  MyClass = public class
  public
    method DoSomething(aParam : Integer);
  end;
 
implementation
 
method MyClass.DoSomething(aParam : Integer);
begin
 
end;

When using class method instead of method, the method will be static, meaning it's called on the class itself and not on one instance of that class. One can only access other class members, not instance members from this method.

Method Parameters

Parameters are declared both in the interface and implementation section. If more than one parameter in a row has the same type, they can be defined with specifying the type only once:

param1, param2 : Integer

Parameters can be provided with a default value, which is used when the parameter isn't passed to the method when calling it:

method Foo(HasToBePassed : String; Bar : Integer := 42);

The default value is only defined in the interface section!

When declaring an array-parameter with the params keyword, the array can be passed as sequence of parameters when calling the method:

method MethodWithManyParameters(params manyParameters : Array of Integer);
// ...
MethodWithManyParameters(1,2,42,34,27);
Modifiers

Modifiers are keywords placed behind the method declaration. They change the behaviour of the method they're applied to. The modifier is not repeated in the implementation section.

There're the following modifiers for methods:

Oxygene Modifiers for Methods
empty The empty modifier tells the compiler, that actually no implementation for this method exists. Therefore no method stub in the implementation section is needed.
method DoNothing; empty;
virtual Virtual methods can be overridden in descendant classes.
abstract The abstract modifier creates an abstract method, which is a method that must be implemented in a derived class. Abstract classes are automatically virtual.
override In a derived class a method with the override modifier overrides a virtual method of the base class.
reintroduce Using the reintroduce modifier will tell the compiler, that you really want to re-implement a base classes virtual method.
final When a virtual method is marked as final in a descendant class, descendants of that class can no longer override the method.
locked Thread-safe methods can be created using the locked modifier. By default, locking works on the entire instance (i.e. self), but one can specify the field to lock on: method LockedMethod; locked on fAField;;
async Additionally one can create asynchronous methods with the async modifier. This means, a call to a method marked with this modifier will immediately return and the code of the method will be executed in a separate thread. The following will output "First" and after half a second the numbers 0 .. 10:
  MyClass = public class
  public
    method DoSomething; async;
  end;
 
implementation
 
class method ConsoleApp.Main;
begin
  var mc := new MyClass;
  mc.DoSomething;
  Console.WriteLine('First!');
  Console.ReadLine;
end;
 
method MyClass.DoSomething;
begin
  Thread.Sleep(500);
  for i : Integer := 0 to 10 do
    Console.WriteLine(i);
end;
external Methods from unmanaged libraries (e.g. the Windows-API) can used when a method is declared with the external modifier and the DllImportAttribute[9].
unsafe Methods marked as unsafe make it possible to use unmanaged code, which for example makes use of pointers.
implements With implements one can specify which method of an interface a method implements:
method CompTo(other: MyClass): System.Int32; public implements IComparable<MyClass>.CompareTo;
The public visibility modifier can be omitted, then the implementation is private (the class has to be explicetly cast into the interface to access the method).
iterator An iterator (in .NET represented by the IEnumerator interface) is an object, which has a method that will return a new element of a sequence every time this method is called. This can be used in a for-each-loop. When giving a method the iterator modifier, the compiler will create such an iterator object from the method. Inside the method you have to use yield to tell the compiler which elements the sequence should consist of:
type
  MyClass = public class
  public
    method GetEvenNumbers: sequence of Int32; iterator;
  end;
 
// ...
 
method MyClass.GetEvenNumbers: sequence of Int32;
begin
  for i: Int32 := 0 to 100 step 2 do
    yield i;
end;
partial Partial methods can be declared as empty methods in one part of a partial class (i.e. they have the modifiers partial; empty;) and implemented in another part of the partial class. If they are not implemented in another part, all calls to the method will be removed at compile time.
Anonymous Methods

Anonymous methods are implemented inside other methods. They are not accessible outside of the method unless stored inside a delegate field. Anonymous methods can use the local variables of the method they're implemented in and the fields of the class they belong to.

Anonymous methods are especially useful when working with code that is supposed to be executed in an GUI thread, which is done in .NET by passing a method do the Invoke method (Control.Invoke in WinForms, Dispatcher.Invoke in WPF):

method Window1.PredictNearFuture;  //declared as async in the interface
begin
  // ... Calculate result here, store in variable "theFuture"
    Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, method; begin
      theFutureTextBox.Text := theFuture;
    end);
end;

Anonymous methods can have parameters, too:

method Window1.PredictNearFuture;  //declared as async in the interface
begin
  // ... Calculate result here, store in variable "theFuture"
    Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, method(aFuture : String); begin
      theFutureTextBox.Text := aFuture ;
    end, theFuture);
end;

Both source codes use anonymous delegates.

Extension Methods

Extension methods are class methods that are treated in a special way by the editor (especially IntelliSense) and the compiler. They're defined and implemented in one class, but are called on another class (at least it looks like if they were).

For example, if you want to define a method Implode, which creates a string representation of a sequence, you can create a class and such a method in it:

  MyClass = public class
  public
    class method Implode<T> (aSequence : IEnumerable<T>) : String;
  end;

You'd call this method like this: MyClass.Implode(someSequence)

With extension methods, you can do it much more elegantly by decorating the method and dedining class with the appropriate attribute to turn the method into an extension method:

  [System.Runtime.CompilerServices.Extension] //namespace can be omitted
  MyClass = public class
  public
    [System.Runtime.CompilerServices.Extension]
    class method Implode<T> (aSequence : IEnumerable<T>) : String;
  end;

No you can call the method directly on any sequence: someSequence.Implode
IntelliSense will show the method on any sequence, too.

At compile time, the method call will be re-written to the "normal" call to MyClass.Implode.

The Extension attribute is defined in the System.Core.dll starting with version 3.5 of the .NET framework. If you don't want to use that version of the framework, you can define the attribute for yourself (in the appropriate namespace) or use Mono's System.Core.dll.

Extension methods are a very important part of the implementation of LINQ.

Fields

Fields store data and can be instance or class members. They should only be used internally, to make a value public, use properties.

MyClass = public class
  fInstanceField : String;
  class var fClassField : Integer;
end;

Fields can be marked as read-only (see sample code below), which means that their value can only be assigned in the (class) constructor. In contrast to constants a field's value will be evaluated at runtime and therefore can be calculated using complex expression.

Initial values of a field can be defined together with the declaration:

fStartTimeAsString : String := DateTime.Now.ToString; readonly;

The compiler will put these initializations into the constructor, normally before calling the inhertied constructor[10].

Delegating interface implementation

Fields can be used to delegate the implemenation of an interface, if the type they're of implements this interface:

Implementor = public class(IMyInterface)
  // ... implement interface ...
end;
 
MyClass = public class(IMyInterface)
  fSomeImplementor : Implementor; public implements IMyInterface; //takes care of implementing the interface
end;

In this example the compiler will create public methods and properties in MyClass, which call the methods / properties of fSomeImplementor, to implement the members of IMyInterface. This can be used to provide mixin-like functionality[11].

Properties

Properties are an abstraction of values describing the object. They often provide controlled access to the value of a field. Properties have a setter and a getter, which are methods called when someone sets / gets the value of the property.

type
  MyClass = public class
  protected
    fStringField : String;
    method GetIntField : Integer;
    method SetIntField(value : Integer);
  public
    property LonelyProperty : Boolean;
    property StringProperty : String read fStringField write fStringField;
    property IntProperty : Integer read GetIntField write SetIntField;
  end;

In the above example three properties are defined. For the first one, the compiler will generate a field implictely and delegate the property's access to this field. For the second property, methods that read and write the value of property from / to the specified field are generated. The field for read and write and access do not have to be the same. Regarding the third property, read and write access are delegated the appropriate methods.

Using Inline Expression one can save one or another method:

type
  Circle = public class
  public
    property Radius : Double := 1.0;
    property Area : Double read Math.Pi * Radius * Radius;  //inline expression
  end;

The example also shows, that the value of properties with implicit fields can be initialized in the interface section.

There can be different visibility levels for read and write access, e.g. using protected write instead of just write.

Properties can be made virtual or abstract by applying the appropriate modifier (virtual or abstract).

Property notification

Property notification is used mainly for data binding, when the GUI has to know when the value of a property changes. The .NET framework provides the interfaces INotifyPropertyChanged and INotifyPropertyChanging (in .NET 3.5) for this purpose. These interfaces define events which have to be fired when a property is changed / was changed.

Oxygene provides the notify modifier, which can be used on properties. If this modifier is used, the compiler will add the interfaces to the class, implement them and create code to raise the events when the property changes / was changed.

property Foo : String read fFoo write SetFoo; notify;
property Bar : String; notify 'Blubb'; //will notify that property "Blubb" was changed instead of "Bar"

As you can see, the modifier can be used on properties which have a setter method. The code to raise the events will then be added to this method during compile time.

Indexers

Indexers are properties that can be accessed as if they were arrays. In Oxygene a class can have an arbitrary number of indexer properties, but only one index property can be the default indexer (which is defined by applying the default modifier).

type
  MyClass = public class
  private
    method GetCell(x, y : Integer): Integer;
    method SetCell(x, y : Integer; value: Integer);
 
    method GetColumn(x : Integer) : Array of Integer;
  public
    property Cell[x, y : Integer] : Integer read GetCell write SetCell; default;
    property Column[x : Integer] : Array of Integer read GetColumn;
  end;
 
// ...
 
var aMyClass := new MyClass();
aMyClass[0,0] := 5;
var aCol := aMyClass.Column[3];

Indexers cannot have implicit getters or setters, these have to be methods or inline expression.

Indexers with the same name can be overloaded by their parameter type and parameter count.

Events

An event is a member of a class or record that others can add delegates as event handlers to, which will be called, when the event is fired. Delegates can also be removed from the event.

In Oxygene one can define an add and a remove method or just which delegate field should be used for holding the event handlers.

Additionally the compiler can generate a "raise method", so that events can be raised from external classes.

type
  MyClass = public class
  private
    fMember : SomeEventHandler; //field for holding event handlers
    method MethodForAdding(param : SomeEventHandler);
    method MethodForRemoving(param : SomeEventHandler);
  public
    event SimplestEvent : SomeEventHandler; //simplest declaration, compiler does the rest
    event SomeEvent : SomeEventHandler delegate fMember; //event handlers will be stored in fMember
    event SomeOtherEvent : SomeEventHandler add MethodForAdding remove MethodForRemoving; //methods will be used for adding and
                                                                                          //removing event handlers
    event YetAnotherEvent : SomeEventHandler raise; //method for external raising of the event will be added
  end;

add, remove and raise can be decorated with a visibility modifier (e.g. protected raise) to limit their visibility.

Event handlers are added and removed using the += operator and -= operator respectively.

Parallel programming

The goal of parallel programming is to use all cores or processors of a computer to improve performance. To reach this goal, tasks have to be distributed among several threads. The .NET framework's ThreadPool class offered a way to efficiently work with several threads. The Task Parallel Library (TPL) was introduced in .NET 4.0 to provide more features for parallel programming. Oxygene provides language-level support for some of these features.

Asynchronous statements

Statements starting with the async keyword will be executed in a separate thread. The following sourcecode will print "foo", then "bar".

async begin
  Thread.Sleep(1000);
  Console.WriteLine('bar');
end;
 
Console.WriteLine('foo');

Asynchronous statements can be assigned to variables, which is useful if you have to wait for an asynchronous statement to finish at one point in your code. The following code will print "bar", then "foo".

var bar := async begin
  Thread.Sleep(1000);
  Console.WriteLine('bar');
end;
bar; //will wait until "bar" finishes
Console.WriteLine('foo');

If the TPL is available it will be used to implement this feature, otherwise the ThreadPool class is used.

(Asynchronous) futures

Futures are not limited to the use in parallel programming, but are most useful when used as asynchronous futures and are therefore described here.

A future is a value that will be calculated at one point in the future. So when accessing it, it may or may not have been calculated already. If it wasn't calculated already when accessing it the first time, the code will wait until the value is available.

The value of a non-asynchronous future will be calculated synchronously on the first access, the value of an asynchronous future will be calculated in the background and when it is accessed the first time, the code will wait for the calculation to finish.

var aFutInt : future Int32 := 42 + 23; //will be calculated when aFutInt is accessed the first time
var aSyncFutInt : future Int32 := async LongCalculation + AnotherLongCalculation; //calculation starts now and will be finished on first access
 
Console.WriteLine(aFutInt); //starts calculation of aFutInt and waits for it
Console.WriteLine(aSyncFutInt); //waits for the already running calculation of aSyncFutInt

You can use type inference for asynchronous futures, so you don't need to declare them as "future T" explicitly.

Similar to the language support for nullables, futures can be used like "normal" values in many cases in Oxygene.

Asynchronous statements are typeless asynchronous futures.

Parallel loops

Parallel loops perform the operations for different iterations in several threads (though normally there won't be as many threads as iterations). The entire loop will only finish when all the threads are finished.

for parallel i : Integer := 0 to 100 do
  CalcSomethingBig(i);
Console.WriteLine('Finished');

The 100 calls to CalcSomethingBig will each be executed asynchronously, but "Finished" will be printed out only after all of them have finished.

Important: There is no guaranteed order in which the calls will be executed! That's also the reason, why downto cannot be used with a parallel loop.

The parallel keyword can be used in combination with for each, too.

Parallel loops need the TPL and won't work with only the ThreadPool class available.

LINQ and sequences

"LINQ" stands for "language integrated query", that are sql-like expressions to work both with "normal" data collections (arrays, lists, ...) and database tables. These queries are not written as strings, like one knows from PHP, but are integrated into the language, providing type safety, compiler checks and IntelliSense. LINQ statements are another syntax for the extension methods provided by the System.Linq namespace, therefore they depend on these methods.

Following is a sample statement:

var u := from u in lUsers
           where u.Age = 35
           order by u.Name;

This is equivalent to:

var u := lUsers.Where(u -> u.Age = 35).OrderBy(u -> u.Name);

The following keywords can be used in LINQ queries: asc, desc, distinct, equals, from, group by, into, join, on, order by, reverse, select, skip, take.

The compiler can emit LINQ queries as expression trees, which can be executed on a database server[12]. This means, that not the entire dataset is fetched from the database and then processes on the client, but the processing is done on the server and only the result is transferred to the client.

Sequences

Sequences represent a collection of data without implying the way this data is stored. A sequence of integer can be an array or a list, or some other collection type. It also can be a type that retrieves new data while the sequence is accessed (e.g. a type fetching bunches of data from a web service).

Sequences are declared as sequence of T and then instantiated by using a compatible collection type (e.g. List<T>):

var cities : sequence of String := new List<string>;
cities := ['Düsseldorf', 'Köln']; //replaces the List<string> with an array of string

Sequences can be used in for each loops or in LINQ statements. When declared as queryable sequence, the LINQ statement in which the sequence is used will be turned into an expression tree.

The "+" operator is supported for sequences and will be realized by using the Concat extension method from the System.Linq namespace.

In iterators iterations can be delegated to sequences by simply yielding the sequence.

Language features

New features in version 3.0

Parallel programming

Oxygene 3.0 introduces a wide range of language concepts that push the envelope for parallel programming and enable developers to create applications that seamlessly scale up for multi-core and many-core systems. This includes support for futures, parallel loops, asynchronous statements, an improved locked directive, and more.

Property notifications

Native language support for property notifications makes it easy to develop solutions that follow the Model/View/Controller design pattern or generally react in well-defined ways to property changes.

Nullable expressions

Oxygene's Expression syntax has been expanded to provide full support for nullable types in arithmetic and other expressions, making the language integration of nullables even more seamless than in 'Joyride'. Improvements have also been made for casting between compatible nullable types, such as assigning a nullbale Int32 to a nullable Int64, etc.

New features in version 2.0 ('Joyride')

Sequences and query expressions

Query Expressions, also known as "Language Integrated Query", or LINQ for short, are a powerful new language feature that allows you to combine the querying capabilities of database languages such as SQL and apply it to any type of data, natively within the Oxygene language.

Queries can be written to work on any type of structured collection, from simple lists and arrays to database tables and other data structures. Combined with the DLinq and XLinq libraries provided by Microsoft as part of the .NET 3.5 runtime, the feature can be used to efficiently query data from database sources and XML documents, without actually retrieving the entire set of data into memory.

var u := from u in lUsers
           where u.Age = 35
           order by u.Name;

Lambda expressions

Mostly used alongside LINQ and query support, lambda expressions provide a unique syntax for passing dynamic expressions as part of method parameters.

var lFiltered := lUsers.Where(u -> u.Age = 35);

Anonymous types

Once again mostly used in LINQ expressions, anonymous types allow you quickly declare unnamed classes within a method body, to group related values in a common entity.

var lUser := new class(Name := 'Peter'; Age := 49);
Console.WriteLine(lUser.Name+' is '+lUser.Age+' years old');

Partial methods

New Partial Method support allows you to define a stub for a method in one part of a class, allowing an optional implementation to be provided by another partial. If no implementation is provided, the method and any calls to it will be omitted from the generated assembly.

Partial Methods make it easy for auto-generated code to define methods that can be implemented by the user. This is used extensively in our new Cocoa# support, as well as upcoming support for LINQ to SQL.

Enhanced nullable types

'Joyride' enhanced support for nullable types, making them a full language feature that fits in neatly with the other types rather than having nullable types feel like a runtime trick. For example, variables defined as nullable Int32 allow full access to members of Int32 and behave like a true Int32 in every sense - with the addition of allowing nil values.

In combination with the new ":" operator (described below) and the newly introduced ValueOrDefault() helper function, nullable types are now easier to use than ever, and feel more natural than in any other .NET language.

Extension methods

Extension methods are a feature introduced by Microsoft in the .NET 3.5 runtime to support LINQ, but can be used in Oxygene in a wide variety of scenarios and on all framework versions.

Simply put, Extension Methods are methods declared in a static class that extend an existing class or interface, and can be invoked on a variable of that type. For example the Where extension method provided by .NET 3.5 extends IEnumerable<T>, and thus can be used on any sequence of objects to filter the collection on an arbitrary condition, even though IEnumerable<T> does not provide a Where member:

var lUsers := array of User;
//...
var lPauls := lUsers.Where(u -> u.Name = 'Paul');

Anonymous methods

Anonymous methods make it possible to specify code assigned to event handlers or passed to delegates right within the body of another method. They not only allow you to skip manually declaring a method within the class, but they also seamlessly allow access to any local variables available within the context where the anonymous method is written.

method SetEventHandler;
begin
  var lNewCaption := 'Clicked!';
  Button1.Click += method(Sender: Object; ea: EventArgs); begin
      Button1.Text := lNewCaption
      MessageBox.Show('the button was clicked.');
    end;
end;

Colon ':' operator

'Joyride' introduces a new operator that can be used anyplace the familiar "." can is used, be it to access a method, properties or other members of a type. Different from the "." operator, which requires the value on its left to be assigned (and will usually throw a NullReferenceException if it is not), the new ":" operator will allow to call members on any value, including nil. If the expression on the left of the colon is nil, the entire expression will automatically short-circuit to return nil as well.

This makes it very easy to access nested members in object hierarchies, when multiple if assigned() checks would otherwise be needed. (BLOG)

var lGreatGrandDadsName := lUser:Parent:Parent:Parent:Name;

'Params' keyword

The params keyword makes it easy to define methods that take a variable number of arguments. By closing the list of parameters with an array parameter prefixed with the params keyword, you can enable callers of your method to pass variable numbers of elements to method calls, which will automatically be converted into an array. This makes it easier to call the method, especially from languages like C#, where constructing an inline array is long and unwieldy.

method Format(aFormat: string: params values: array of Int32);
//...
Format('...', 1, 2, 3, 5, 27);

'implies' operator

The new implies operator was designed specifically for require/ensure clauses and class invariants, but can also be used elsewhere in code. Similar to and or, it combines two boolean expressions; the difference is that with implies, the second expression is only evaluated if the first/left expression is true. if the first expression is false, the entire expression will be considered true.

ensure
  HasDriversLicense implies Age >= 16;

Type inference for 'for each' loops on generic sequences

For 'Joyride', for each loops have been improved to automatically infer the type of the loop variable when working on a generic IEnumerable<T> or other strongly typed sequence, avoiding the need to manually specify the type name. As a side effect, for each loops will now always implicitly define their loop variable.

var lUsers: array of Users;
for each u in lUsers do
  Console.WriteLine(u.Name); // Compiler knows "u" is a "User"

For non-generic enumerations (IEnumerable), a explicit type declaration will be required inside the for each loop; the compiler will not automatically infer to use System.Object.

'index' operator for 'for each' loops

The Syntax for each loops has been expanded in 'Joyride' to allow for an optional index variable to be defined, which will count from 0 through the number of elements looped. This is helpful in scenarios where the number of elements processed is needed as part of the loop, be it to access a separate collection by index, or to use different code to handle the first or even/odd elements.

When using for each matching or other mechanisms (such as LINQ) to filter down the collection, the index will only count those elements that actually execute the loop.

for each u in Users index i do begin
  if i > 0 then Console.Write(';');
  Console.Write(u.Name);
end;

New features in version 1.5 ('Floorshow')

Generic methods

Use Generics to implement strongly typed methods with and parameterized types (.NET 2.0 only).

Iterators

Easily implement collections and enumerable classes using iterators for both .NET 1.1 and 2.0 using the new 'iterator' directive and the 'yield' keyword.

method CountTo100: Int32; iterator;
begin
  for i: Int32 := 0 to 100 do yield i;
end;

Nullable types

Avoid boxing by using new nullable value types on the .NET 2.0 framework. Support for nullable types has been vastly enhanced for 'Joyride' (see above).

var i := nullable Int32;
if assigned(i) then i.CompareTo(5);

Nested types

Define and implement nested types using Oxygene's new and intuitive 'nested in' syntax

type
  HelperClass nested in MainClass = class //...

Dual-visibility for properties

Define properties with different visibility levels for getter and setter, for example allowing public reading and protected writing of properties:

property Count: Int32 read fCount protected write SetCount;

Extended constructor calls

Create objects and initialize properties in one statement

var b := new Button(Width := 150, Height := 25);

Fixed size buffers

Use Fixed Size Buffers to declare efficient inline arrays inside your records, in "unsafe" code.

New features over object pascal in version 1.0

Generic types

Use Generics to implement strongly typed containers and parameterized types.

Class contracts

Oxygene is the first mainstream .NET language to provide native support for Design By Contract like constructs, with pre-conditions, post-conditions and invariants.

Namespace support

Namespaces are one of the great basic concepts of the .NET framework that most developers take for granted. Oxygene provides three basic features that allow developers to work with namespaces.

Virtual properties

Virtual Properties and Events allow you to more easily define abstract classes and interfaces, or overwrite existing framework interfaces that contain properties.

Enhanced events support

Oxygene introduces a new syntax for defining and working with events to the Object Pascal language.

Asynchronous methods and thread synchronization

Easily write multi-threaded applications using Oxygenes' async keyword and asynchronous methods. Use the locked and locking keywords to write thread-safe applications.

Partial classes

The only .NET compiler to provide partial classes support for .NET 1.1.

Operator overloading

Make your classes intuitive to use by providing custom operator overloads for common operations such as addition or subtraction.

Class references, virtual constructors

Easily implement the Factory Pattern or dynamically create object instances using Oxygene's Class References (Meta Classes).

Enhanced loops

Oxygene enhances the classic for/to loop to allow inline declaration of the loop variable type. It also provides a new for each loop type for looping across .NET IEnumerable and IEnumerable<T> types, as well as an infinite loop loop.

Inline variable declarations and type inference

Declare new variables using the 'var' statement inside your method bodies to keep them with the code that uses them. Avoid retyping type names and let Oxygene infer new variable types from the assigned value.

Inline property readers

Use inline code to implement simple property readers, such as

property Foo: String read 'Bar';

Enhanced 'case of' and 'case type of' statements

'case' statements can use strings or other non-ordinal types, as well as the new 'case type of' statement to execute different cases depending of an object's type.

case aClassID.ToUpper of
   'XYZ': result := TMyXYZClass;
   'ABC': result := TMyOtherClass;
  else raise new Exception('Invalid Class ID');
end;
 
case aClass type of
   TMyXYZClass: TMyXYZClass(aClass).DoSomething;
   TMyOtherClass: TMyOtherClass(aClass).DoSomethingElse;
   else raise new Exception('Invalid Class Reference');
end;

Enhanced 'try/finally/except'

Combine 'finally' and 'except' to create more concise and efficient exception handling code.

Exception filters

Previously only available in Visual Basic .NET, Exception Filters provide extended flexibility for catching exceptions over common 'try/except' blocks.

Boolean double comparisons

Easily compare values against boundaries with statements such as

if 0 <= x < Count then //...

Empty methods

Quickly define class interfaces to flesh out later or empty methods to be overridden in descendant classes.

Static classes

Implement static classes that cannot be instantiated at runtime, but provide static functionality to your project.

Enhanced 'exit' statement

Use the improved exit statement to terminate methods and set a return value in one step.

Enhanced 'is not' and 'not in' statements

Use the new 'is not' operator to write more readable type check statements, and 'not in' for improved set handling.

Differences between native Delphi and Oxygene / Delphi Prism

Deprecated keywords

  • unit: Replaced with the namespace keyword. Since Oxygene doesn't compile per-file but per-project, it does not depend on the name of the file. Instead the unit or namespace keyword is used to denote the default namespace that all types are defined in for that file
  • procedure and function: These two keywords have been replaced with the method keyword.
  • overload: In Delphi Prism all methods are overloaded by default, so no special keyword is needed for this.
  • .Create(): This constructor call has been replaced by the new keyword. It can still be enabled in the project options for legacy reasons.

Criticism

Some people would like to port their Win32 Delphi code to Prism as is. This is not possible because while Prism looks like Delphi there are enough changes to make it incompatible for a simple recompile. So while the name makes you think it is just another version of Delphi that is not completely true.[13]

On top of the language differences the Visual Component Library framework is not available in Delphi Prism.[14] This makes porting even more difficult because classic Delphi code relies heavily on the VCL.

Code examples

Hello World

namespace HelloWorld;
 
interface
 
type
  HelloClass = class
  public
    class method Main; 
  end;
 
implementation
 
class method HelloClass.Main;
begin
  System.Console.WriteLine('Hello World!');
end;
 
end.

Generic container

namespace GenericContainer;
 
interface
 
type
  TestApp = class
  public
    class method Main;
  end;
 
  Person = class
  public
    property FirstName: String;
    property LastName: String;      
  end;
 
implementation
 
uses 
  System.Collections.Generic;
 
class method TestApp.Main;
begin
  var myList := new List<Person>; //type inference
  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 casting needed
  Console.ReadLine;        
end;
 
end.

Generic method

namespace GenericMethodTest;
 
interface
 
type
GenericMethodTest = static class
public
  class method Main;
private
  class method Swap<T>(var left, right : T);
  class method DoSwap<T>(left, right : T);
end;
 
implementation
 
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;// type inference
  var b := 15;
  DoSwap<Integer>(a, b); // no downcasting to Object in this method.
 
  var aa := 'abc';// type inference
  var bb := 'def';
  DoSwap<String>(aa, bb); // no downcasting to Object in this method.
 
  DoSwap(1.1, 1.2); // type inference for generic parameters
  Console.ReadLine();
end;
 
class method GenericMethodTest.Swap<T>(var left, right : T);
begin
  var temp := left;
  left:= right;
  right := temp;
end;
 
end.

Program Output:

Type: System.Int32
-> a = 23, b = 15
-> a = 15, b = 23
Type: System.String
-> a = abc, b = def
-> a = def, b = abc
Type: System.Double
-> a = 1,1, b = 1,2
-> a = 1,2, b = 1,1

---

See also

References

External links


Wikimedia Foundation. 2010.

Игры ⚽ Поможем написать курсовую

Look at other dictionaries:

  • Pascal (programming language) — Pascal Paradigm(s) imperative, structured Appeared in 1970 Designed by Niklaus Wirth Typing discipline static, strong, safe …   Wikipedia

  • Ada (programming language) — For other uses of Ada or ADA, see Ada (disambiguation). Ada Paradigm(s) Multi paradigm Appeared in 1980 Designed by MIL STD 1815/Ada 83: Jean Ichbiah Ada 95: Tucker Taft Ada 2005: Tucker Taft Stable release …   Wikipedia

  • C Sharp (programming language) — The correct title of this article is C# (programming language). The substitution or omission of the # sign is because of technical restrictions. C# Paradigm(s) multi paradigm: structured, imperative …   Wikipedia

  • F Sharp (programming language) — The correct title of this article is F#. The substitution or omission of the # sign is because of technical restrictions. F# Paradigm(s) multi paradigm: functional, imperative, object oriented Appeared in 2002 (2002) …   Wikipedia

  • Oxygene — may refer to: Oxygene (programming language) Oxygene (album), an album by Jean Michel Jarre, released in 1976 Oxygene 7–13, an album by Jean Michel Jarre, released in 1997 Oxygene: New Master Recording, a new edition of Jean Michel Jarre s 1976… …   Wikipedia

  • Comparison of programming languages — Programming language comparisons General comparison Basic syntax Basic instructions Arrays Associative arrays String operations …   Wikipedia

  • List of programming languages — Programming language lists Alphabetical Categorical Chronological Generational The aim of this list of programming languages is to include all notable programming languages in existence, both those in current use and historical ones, in… …   Wikipedia

  • List of programming languages by category — Programming language lists Alphabetical Categorical Chronological Generational This is a list of programming languages grouped by category. Some languages are listed in multiple categories. Contents …   Wikipedia

  • Language Integrated Query — LINQ redirects here. For the card game, see Linq (card game). Language Integrated Query Influenced by SQL, Haskell Language Integrated Query (LINQ, pronounced link ) is a Microsoft .NET Framework component that adds native data querying… …   Wikipedia

  • Common Intermediate Language — For the counterpart to compiled assembly in the Microsoft .NET framework, see .NET assembly. Common Intermediate Language (CIL, pronounced either sil or kil ) (formerly called Microsoft Intermediate Language or MSIL) is the lowest level human… …   Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”