CSharp vs. Oxygene
The obvious differences between Oxygene and C# is the of course Object Pascal based syntax, which is very attractive for developers skilled in other Object Pascal dialects, such as Delphi, FreePascal, or other Pascal dialects. However, the language adds a wide range of other extremely useful enhancements as well:
See also Java vs. Oxygene.
The following is a list of language features that are available in Oxygene and not available in C#:
|Named indexed properties||
C# can only access the default indexed properties. In Oxygene, you can define and use other indexed properties using their name.
A set is a collection of ordinal values of the same type. Sets are a convenient way of storing and accessing values that belong together.
Supply the async directive on a method and it will automatically run in a separate thread. If the method returns values, these will become futures.
This is essential to our nullable support, but it is also a surprisingly useful feature for general coding. Basically, instead of using "." to access members and get a NullReferenceException if they are nil, use ":" and the call will be skipped for nil (and the result will be nil itself). For example:
// only call dispose if the interface is implemented. IDisposable(SomeObject):Dispose; // access the 3rd parent of a control, where any level might be nil. x := Button:Parent:Parent:Parent;
|Inline Property Accessors||
Define simple properties within the class declaration, without any extra setters/getters.
property Foo: String; // local field is implied; property Right: Int32 read Left+Width; // reader expression is provided in line.
Define "class of X" types and virtual class methods (similar to Delphi for Win32).
This is mostly helpful for invariants or pre/post-conditions. Basically, the second part only "needs" to be true if the first is; if the first is false, the entire expression is true. this sounds odd, but consider
require assigned(Customer) implies Customer.CreditRatio > 5;
this translates too
require (NOT assigned(Customer)) OR (Customer.CreditRatio > 5);
the above is much more readable.
|"case type of"||
Write a switch/case statement that executes different cases depending on the TYPE of a certain object, i.e.
case type MyControl of: Edit: // do something Button: // do something else end;
These save a lot of boilerplate code. Attach the "notify" keyword to a property as directive (like "virtual"), and the compiler generates all the infrastructure and interfaces needed for property notifications to work (in C#, you'll need to implement getters/setters, add interfaces to the class, etc).
|Enhanced Nullable Types||
Unlike C#, the Oxygene compiler provides full support for nullable types. You can call their members and use them in expressions; there's extensive support for nullable expressions - if any part that contributes to an arithmetic expression is "nullable", the whole expression becomes nullable. if any part IS null, the whole expression becomes null. For example:
x: nullable Int32; y: Int; var a := x+y; // result is a nullable Int32; var b := 5*x; // if x is null, so is b.
|improved 'for each' loop||
for each matching x: Button in Controls do // only run loop for buttons
// use i to count; also type of x is inferred automatically // from type of "Controls", if its a generic enum for each x in Controls index i do
The locked directive allows methods, events and properties to be declared implicitly thread safe.
|Extended Constructor Calls||
Set properties as part of the constructor call. This is especially useful if you're not assigning to a local ref. For example:
Controls.Add(new Button(Width := 100; Height := 50));
without this, you; would need a temp var for the button.
This is now possible with C# 3.0.
|Boolean Double Comparison||
if 0 <= x < Count then //...
The yield keyword supports delegation of the iterator to a second sequence.
Supply the 'empty' directive on a method's interface declaration and you dont need to define the empty method body.
Catch selective exceptions not only based on type, but also based on any arbitrary condition (supported by VB.NET, but not C#).
You can specify the visibility for raising/firing events. In C#, only the class that defines an event can fire it (from outside you can only add/remove handlers). In Oxygene, you can write the following and descendants can fire the event:
public event Foo:EventHandler protected raise;