Minor Language Differences compared to Delphi

From The Elements Wiki
Jump to: navigation, search
Delphi

This topic is all about moving from Delphi to Oxygene.
 

(TODO: this page is considered to be complete, but is pending final verification for full accuracy)


Contents

Aside from the vast amount of major language features 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 namespace 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 uses 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 .fx files 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.

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 StrUtilsunit. 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 [Class Member 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 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 Operator

Oxygene uses nameless constructors (and, on Cocoa, 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 new 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 keyword method 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 or 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 method Foo(a: Int32; b: String := 'default');. This is to illustrate that it's really a default assignment to the parameter that is happening here, not an expression of equality.
  • Similarly, fields, properties and local variables can be pre-initialized using a fMyField: Int32 := 5; syntax; differing from Delphi's const 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 var works to define a variable, whether a type is specified or omitted - a feature we call Type Inference).

Unsupported Method Flags

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, inline, 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 and deprecated "cross-platform" warning flags are not supported in Oxygene.
  • The static flag is not supported in Oxygene to indicate static methods; instead, the class keyword is used consistently.
  • 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 Language Compatibility Options.


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 where 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

  • Use Delphi-compatible division operators — Changes the div and / operators to behave as they do on Delphi, with div always producing an integer result, and / always producing a float result, regardless of input.

Improved with Construct

Oxygene drops Delphi's inherently unsafe with construct and replaces it with a new with 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 might be an appropriate solution, and are available on all three 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.

No Resourcestrings

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 be rewritten to run fully managed and verifiable, with a little effort - 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).

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 requires MyArray[dim1][dim2], for an array[0.., 0..] of Integer it requires the MyArray[dim1, dim2] syntax.
  • Variant records are not directly supported in Oxygene, except on Cocoa.
  • Oxygene has no goto statement, and likewise no label. It is the 21st century; you really should not be needing goto.

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.