Record constructor in Delphi is a language feature that arises questions – what it is and when to use it instead of a record method. If you look at Delphi documentation you will find the following:
Records are constructed automatically, using a default no-argument constructor, but classes must be explicitly constructed. Because records have a default no-argument constructor, any user-defined record constructor must have one or more parameters.
The above documentation is nonsence. There is no such a thing as ‘a default no-argument constructor’ in Delphi. That arises one more question – why Delphi does not allow parameterless record constructors while allowing record constructors with parameters.
Record constructor in Delphi is a special syntax for a record method.
Suppose you are writing an advanced record that implements a complex number and need some initialization method. You may guess what is better, functional form:
type TComplex = record Re, Im: Double; function Create(ARe, AIm: Double): TComplex; end; function TComplex.Create(ARe, AIm: Double): TComplex; begin Result.Re:= ARe; Result.Im:= AIm; end;
or procedural form:
type TComplex = record Re, Im: Double; procedure Create(ARe, AIm: Double); end; procedure TComplex.Create(ARe, AIm: Double); begin Re:= ARe; Im:= AIm; end;
With the record constructor syntax you get both:
type TComplex = record Re, Im: Double; constructor Create(ARe, AIm: Double); end; constructor TComplex.Create(ARe, AIm: Double); begin Re:= ARe; Im:= AIm; end;
you can call record constructor as a function
var C: TComplex; begin C:= TComplex.Create(0, 0); end.
or as a procedure
var C: TComplex; begin C.Create(0, 0); end.
Both forms are correct.
The above example is oversimplified and may seem artificial, but sometimes an option to call a record method either as a function or as a procedure is useful and handy.
The call on an “instance” (as a procedure) also exists for classes, but it looks weird because of the naming, and IME is more often a bug (unintended) than something desired. There can also be various side-effects, as a constructor is typically written to be a… constructor, and not a setter.
Other languages have it right: constructors can only be invoked on instances from within a constructor, and then, once and only once.
That is for class constructors. Record constructors are different – there is nothing wrong in calling record constructor as a procedure in Delphi.
Careful – a “class constructor” is something else again !!
Um, that depends on how the “record constructor” is written.
There is sometimes “nothing wrong” with calling a constructor as a procedure on an instance (of a class) in Delphi either. It will rarely be the right thing to do however because as Eric says, the author of the constructor typically is not treating the constructor as an initialisation procedure but as an instantiation callback. e.g. constructors that instantiate other, internal objects usually do so unconditionally – they do not allow for the possibility that they may already be created and that the referenced objects need only to be re-initialised.
The same could equally be true of the author of a record constructor. It all depends on what the constructor does and how it is written.
Given that Delphi has a long an illustrious history paved with use of constructors as instantiators, it seems advisable to continue to follow that pattern for consistency.
Jolyon said it well, a constructor will typically be written as an instantiation.
f.i. in a record, managed fields (strings, dynamic arrays, interfaces) are always initialized by default.
In a “constructor” used for instantiation, you don’t have to set strings to the empty strings, SetLength() of dynamic arrays to zero or nil interfaces.
In a “constructor” used for initialization, you would have to reset all those fields.
Using C := TComplex.Create tempts you into following with FreeAndNil(C). That ends painfully,
Indeed – convention is a powerful friend, but can also be a sneaky enemy. 🙂
I should have added to my original comment that since “constructors” on records are not associated with instantiation, only initialisation, then the temptation to treat them as constructors as we are more familiar with should be resisted.
The use of the word “Create” – or any near equivalent – in a record “constructor” should be avoided. The keyword “constructor” similarly should not have been allowed within a million miles of the record declaration syntax.
Instead they should have used the “initialization” keyword. Too late now.
I got pinged this week in exactly this way with TRegEx.Create. Serves me right for using FreeAndNil I suppose!!!
So is it safe to say that a record constructor is more like an initializer or a class method (with access to instance fields) than a constructor? The difference is supposed to be whether you are calling it from an instance of the record, or from the class type of the record?
TMyRecord = record
constructor Create(X,Y: Integer);
constructor TMyRecord.Create(X,Y: Integer);
FX := X;
FY := Y;
function DoSomething: Integer;
// or can the constructor only be used…
// Values := TMyRecord.Create(20,10);
Result := Values.FX – Values.FY;
I’m currently messing around with the new TVertexBuffer class, trying to set vertex data and running into to this ambiguity on the TPoint3D record’s version of the Create constructor (which looks just like an initializer.
Good article. Thanks for sharing!
Pingback: Конструкторы записей (record) в Delphi | DelphiFeeds.ru 2.0