Minor Differences
Aside from the vast amount of New Language Features compared to Delphi that Oxygene brings to Object Pascal, it also provides some minor cleanup of idiosyncrasies to make the language more consistent and a better citizen on the (semi-)managed platforms. This topic describes these "cleanups" in more detail.
True Namespace Support
Oxygene has full support for Namespaces. As such, it does away with the unit keyword used in Delphi, and each source file starts with the keyword instead, optionally followed by a namespace name. All types declared in a unit are part of the same namespace (unless the type declaration provides a full alternative namespace), and multiple source files can and usually will contribute to the same namespace — in fact, it is even common for small to medium projects to place all their types into a single namespace.
The clause syntax persists as in Delphi, but instead of listing units to be used, it will list the namespaces that you want to be in scope for the current source file. Any types declared in either the current namespace or a namespace that is "used" will be visible within the source file without specifying the full namespace.
It is important to distinguish between Namespaces and
References. Using a namespace only brings types
into scope that are already known to the compiler, so that they can be
identified by their short name. References added to the project (.dlls
on .NET, .jar files on Java and on Cocoa) in turn give the compiler a
list of places to find types in. Often, there's a direct mapping between
a reference and a namespace (UIkit.fx, for example, contains the
classes in the UIKit
namespace), but sometimes that is not the case.
Read more on Namespaces and References.
0-Based Object-Oriented Unicode String
In Oxygene, the standard String
type maps to the platform's default string class, which contains immutable, zero-based unicode strings. On
.NET that is System.String
, on
Cocoa Foundation.NSString
and on
Java java.lang.String
.
Because Strings are true objects, they provide member methods and
properties you can call to perform string manipulations, mostly
obsoleting helper libraries such as Delphi's StrUtils unit. Another
important consideration is that, because strings are regular objects,
the language differentiates between a nil
string reference vs. an empty
(''
) string.
Oxygene allows the use of either single ('Hello'
) or double
("World"
) quotes for string literal declarations.
Proper Private/Protected Visibility
In Oxygene, the private
and protected
Visibility Levels truly have the visibility implied by their names: private members are truly private to
the individual class; protected members are only accessible to
descendant classes. In Delphi, both of these keywords allow unchecked
access to private and protected members of all classes in the same unit
— which is unclean.
Recent versions of Delphi have introduced new strict private and
strict protected visibility sections that mimic what Oxygene's private
and protected
visibility types do out of the box.
The unit
and unit or protected
visibility can be used to obtain an
effect similar to Delphi's interpretation of private/protected.
Delphi also supports a published visibility type that behaves mostly identical to public. This visibility is not supported or needed in Oxygene.
Nameless Constructors and the new
Keyword
Oxygene uses nameless constructors (and optionally constructors with Cocoa's with* naming convention) instead of Delphi's convention of using regular method names, commonly starting with Create. This goes along with the operator, which is used for instantiating objects.
The use of Create is not supported in Oxygene by default, neither for declaring constructors, nor for calling them.
No Destructors, but Finalizers
Oxygene does not support the concept of destructors, since all of its supported platforms use Garbage Collection or Automatic Reference Counting. As such, the destructor keyword is not supported.
As a slightly related concept, but not 100% comparable to destructors, Oxygene supports Finalizers to allow objects to perform cleanup when they are released. Unlike Delphi's destructors, finalizers will be automatically called by the runtime as needed, and will/should never be called explicitly.
Methods
While still supported, Oxygene deprecates the procedure and function
keywords and favors the method
keyword to be used for all method declarations.
The reasoning for this is two-fold. For one, Pascal traditionally calls things by their name — and Methods are something fundamentally different than the actual functions and procedural of pre-OOP procedural programming. For another, in modern Pascal it seems unnecessary and arbitrary to distinguish between methods that return a result and those that do not.
:=
vs =
Operator
Oxygene employs the standard Pascal :=
assignment operator in two
places where Delphi uses the plain =
operator:
-
Default values for method parameters are indicated using
:=
, such as:
This is to emphasize that it's really a default assignment to the parameter that is happening here, not an expression of equality.method Foo(a: Int32; b: String := 'default');
-
Similarly, fields, properties and local variables can be
pre-initialized using a
fMyField: Int32 := 5;
syntax; differing from Delphi'sconst fMyField: Int32 = 5
syntax. Once again, the point is that the field is not a constant, but merely pre-initialized and that an assignment is being expressed, not an equality.
Oxygene continues to use =
for actual constant declarations, such as
const MY_CONST = 5;
, as here a constant is declared to be
equal to a given value.
The syntax for so-called "typed consts" is supported, but members
defined with this syntax are pure constants and cannot be modified; in
essence, const
works the same whether a type is specified or not (symmetrical
to how it works to define a variable, whether a type is specified or omitted - a feature we call Type Inference).
Unsupported Member Modifiers
Delphi supports a plethora of method flags that are unnecessary or have no relevance on the platforms supported by Oxygene, and are thus not supported. These include:
-
The
stdcall
,cdecl
,pascal
,register
,safecall
flags, used in Delphi to indicate the lower-level binary calling convention to use for the method, are not supported or necessary in Oxygene. -
The
overload
flag is not supported or necessary in Oxygene, method overloading is supported by default. -
The
library
,platform
anddeprecated
"cross-platform" warning flags are not supported in Oxygene. -
The
dynamic
keyword, used by Delphi to indicate an alternative technique for method virtualization, is not supported in Oxygene. - The
reference to
keywords are not supported.
All of these keywords can be used (in most cases to be ignored) via the Delphi Compatibility Settings.
Implicit var
/out
in method calls
In Oxygene, when passing values by reference to a method declared with
the var
or out
keyword, it is necessary to prefix the passed parameter with the
matching var
or out
keyword. This makes sure that it is obvious from the call
site that the parameter is passed by reference, and can be modified by
the called method.
In Oxygene for Cocoa, the var
or out
keywords can also be
used to call framework APIs that are defined to take object pointers – which essentially are C's and Objective-C's way of passing by reference.
Generics
Recent versions of Delphi have begun implementing support for Generics, and the basic syntax for declaring and using them – via type parameters in angle brackets – is the same in Oxygene and Delphi. Unfortunately, while Delphi borrowed the basic syntax from Oxygene, which brought it to the Pascal landscape first, Embarcadero chose to use a different syntax for declaring Generic Constraints.
Oxygene uses the keyword and a rich syntax for declaring the various
different types of constraints, such as is IFoo
, or
has constructor
. It does not support Delphi's constraint syntax.
Different behavior of the div
and /
Operators
In Oxygene, the div
and /
division operators always derive their result from the type of the input parameters. Dividing two integers will result in an integer; dividing floats will result in a float. This is consistent with how all other operators behave as well.
The "Use Delphi-compatible division operators" project option can be used to change this behavior. See Delphi Compatibility Settings for details.
Improved with
Construct
Oxygene drops Delphi's inherently unsafe with construct and replaces
it with a new construct that forces the declaration of a new variable
name to access the scope of the with
clause. This preserves many of
the benefits of the with
feature as found in Delphi, without the
dangerous scope conflicts.
Of course, the Inline Variable Declarations support in Oxygene makes the new with
rarely used these days.
No initialization and finalization Sections
Oxygene does not provide support for initialization and finalization sections, nor any similar functionality because no concept exists on the underlying platforms that would allow to reliably reproduce the functionality provided by these features in Delphi - namely to execute particular code at startup or shutdown of the application.
Depending on your design goal, there are several avenues to consider for providing the necessary functionality. If the purpose of the initialization section is to initialize a type or types defined in the module, [Class Constructors](Class Constructors) might be an appropriate solution, and are available on all platforms. If you are trying to register types or information for later consumption, consider using Custom Attributes (on .NET and Java) or other infrastructure provided by the runtimes on all three platforms for querying available classes.
Delphi-style GUIDs in Interface Declarations
Oxygene does not support Delphi-style GUIDs in Interface declaration, unless the Delphi Language Compatibility
Options are turned on. You can use the [Guid]
Aspect, instead.
No Resource Strings
The resourcestring
keyword is not supported in Oxygene. Each of the platforms targeted by Oxygene has unique (and usually intuitive and simple to use) ways to deal with localized strings, but they are not tool-chain compatible with having resources strings defined in code.
See Localizing Applications for platform-specific topics on how to deal with localization.
Pointers and "Unsafe" Code
As a managed environment, .NET and Java provide limited support for pointers and directly manipulating memory. In general, this is not a problem and most code that relies on pointers can, with a little effort, be rewritten to run fully managed and verifiable - this is the recommended approach.
On .NET, Oxygene supports writing so-called Unsafe
Code by setting the appropriate project option
and marking methods with the unsafe
keyword. The term "unsafe" here does not reflect that such code is
inherently bad or broken, just that it (potentially) performs memory
actions that cannot be verified as "safe" by the runtime. As such, the
runtime may impose restrictions on unsafe code, for example when running
applications from the network, in specific host environments, or in
other low-trust scenarios, such as on phones or on WinRT.
Where possible, unsafe code should be avoided, but the option is there if needed. Please refer to the Unsafe Code topic for more details on this.
On Cocoa, which we sometimes like to refer to as semi-managed, pointers and other code constructs common for "unmanaged" code are available.
On Java, "unsafe" code is not supported at all.
Interface Method Renaming
Delphi for Win32 uses the "=
" operator to implement interface methods
under a different name should conflicts arise, such as:
procedure MyFooBar;
procedure IFoo.bar = MyFooBar; // maps the MyFooBar method to the IFoo interface
Oxygene does not provide this syntax, but uses the implements
keyword to achieve this
(and provide a lot more flexibility in the process). Refer to the Interface topic for more details.
Record Initializers
Oxygene uses named parameters to initialize a record and class fields and properties.
var x := new MyRecord(Field1 := 'test', Field2 := 15.2)
This syntax works in both definition and in code blocks.
Delphi does not have a syntax for this that works inside blocks, but it does have one for constants:
const p: TPoint = (x: 15; y : 16);
This syntax is not supported in Oxygene.
Minor Items
- Oxygene does not allow access to the outer result variable from inside a nested local method.
- Oxygene does not allow you to re-declare a local variable in a nested method, if a variable of the same name is also declared in the outer method.
-
You are not allowed to compare Booleans with the
>
and<
operators. -
Oxygene requires the exact number of array parameters, as the array
defines when accessing array members. For an
array of array of integer
it requiresMyArray[dim1][dim2]
, for anarray[0.., 0..] of Integer
it requires theMyArray[dim1, dim2]
syntax. - Variant records are not directly supported in Oxygene, except on Cocoa.
Inline Assembler Code
Since Oxygene compiles against many different target platforms,
including IL, JVM, x86, x64 and ARM, it does not provide support for the
asm
keyword and inline assembler code.