Constructors

From RemObjects Software

Jump to: navigation, search

This is a Oxygene Language Feature topic
Feel free to add your notes to this topic below.



Constructors (or in short: ".ctors") are special methods that are used to create and initialize instances of a class.

.NET does not provide the concept of individual names for constructors, so Oxygene uses unnamed constructors.

You can create several overloaded constructors that expect different parameters and your constructors can call each other to defer parts of the initialization:

type
  MyClass = class
  public
    constructor;
    constructor(aValue: String);
  end;

...

constructor MyClass;
begin
  ... // instance initialization goes here.
end;

constructor MyClass(aValue: String);
begin
  constructor; // call other constructor
  ... // initialization based on aValue.
end;

constructors are called by using the the new keyword with the class type name:

lClass := new MyClass();

Any parameters expected by one of the constructors can be passed as part of the invocation:

lClass := MyClass.Create('hello');
lClass := new MyClass('hello again');

Note the use of 'Create' above. This is the equivalent of 'new' and is provided for backward compatibility only (see Project Options - Compatibility).


Class Constructors

In addition to constructors for class instances, you can also provide a "class constructor" implementation, which will be called by the framework the first time your class is ever accessed.

In contrast to instance constructors, you can only provide one non-overloaded and parameter-less class constructor. You will never call these explicitly (can't, in fact), but the runtime will call them for you.

class constructor MyClass;
begin
  ... // class initialization goes here.
end;


Constructors and Inheritance

When creating new descendant classes in Oxygene, the compiler will automatically make all (non-private) constructors implemented in the base class available for the new class.

This is in contrast to C#, where a descendant class will need to re-implement all relevant constructors - even if they would just be empty and call the inherited version; in Oxygene, this is not necessary.

Once you start implementing one or more custom constructors for your descendant class (whether replacing an existing one or adding a newly overloaded one), the compiler will no longer make the base constructors available.

The reasoning behind this is that if one or more custom constructor implementations are present, it is safe to assume that none of the remaining base constructors are sufficient to properly construct and initialize the class. For example, EOIDError might depend on the ID and on code that gets executed in your new constructor, but if the base constructor was also available, callers could completely circumvent your own constructor logic. If this weren't done, you would be stuck with the constructors defined in your base class, and never ever be able to get rid of them.

Calling other constructors

In your constructor implementation, you must call an inherited constructor (or another constructor that calls an inherited constructor) before you can access members of your class. However, if there's a MATCHING constructor in the base class, Oxygene will call that automatically for you when no explicit constructor calls are made.


Extended Constructor Calls

Very often the first thing to be done after creating a class is to initialize some of its fields and properties. For example:

with p := new Point(param1, param1) do begin
    p.x := 10;
    p.y := 20;
    // other code
  end;

Oxygene allows you to add named parameters after the normal constructor parameters. These are in the form name := value thus allowing the code above to be replaced by:

with p := new Point(param1, param1, x := 10, y := 20) do begin
    // code
  end;

This extended syntax is only supported for constructors and the named parameters can only be placed after the normal parameters.


Field Initialization

The time that a constructor initializes its fields is important and this can occur before or after the call to an inherited constructor.

Fields are normally initialized before calling the inherited constructor, but with the following three exceptions when the field:

  • uses SELF directly
  • accesses field(s) of the ancestor class
  • calls a method of the current class

Consider the following code:

type
  Foo = class
  private
    Field1: string := 'hello';
    Field2: &Type := self.GetType();
  public
    constructor;
  end;

implementation

constructor Foo;
begin
  // Field1 will be initialized here
  Console.WriteLine(Field1);
  Console.WriteLine(Field2.Name); // error
  inherited constructor;
  // Field2 will be initialized here
  Console.WriteLine(Field2.Name);
end;

Because Field2 uses "self" in its initialization, it cannot be initialized until after the base constructor was run. Field1 doesn't rely on self, so it will be initialized at the beginning and is accessible from the start.

Notes

For backward compatibility, you can use the keyword "Create" as a standard name for constructor methods. You have to enable this option in the Project Options, see Legacy Create Constructors.

type
  MyClass = class
  public
    constructor Create;
  end;

...

constructor MyClass.Create;
begin
  ... // instance initialization goes here.
end;


Examples

Example 1

type
  ClassA = public class
  public
    constructor();
    constructor(aValue: String);
  end;

  ClassB = public class(ClassA)
  end;

Because ClassB doesn't implement any constructors of its own, both constructors from ClassA are made available and the following are both valid:

myObject := new ClassB;          // ok
myObject := new ClassB('hello'); // ok

Example 2

type
  ClassC = public class(ClassA)
    constructor(aValue: Int32);
    constructor(aValue: Char);
  end;

ClassC implements its own constructors, so only these are available:

myObject := new ClassC;          // error
myObject := new ClassC('hello'); // error
myObject := new ClassC(5);       // ok
myObject := new ClassC('a');     // ok

Example 3

Your class defines two constructors and you want to call one from the other

without using the old "create" syntax. You can now write:

constructor MyClass(A: Integer);
begin
  inherited constructor();
  fA := A;
end;

constructor MyClass(A: Integer; B: String);
begin
  constructor(A);
  fB := B;
end;


See Also


Product: RemObjects Oxygene (formerly known as Chrome)
Current version: 3.0 Previous Versions: 'Joyride' (2.0), 'Floorshow' (1.5), 'Adrenochrome' (1.0)

GlossaryKeywordsLanguage FeaturesPlatform FeaturesSamplesArticlesHow ToIssues

Personal tools