views:

357

answers:

3

If I define a record, I can define an instance of that record as a constant record e.g.

Tunit = record
  _name: string;
  _scale: real;
end;

const metre: Tunit =
  (_name: 'metre';
   _scale: 1.0;
  )

I need to do something similar where Tunit is a Class and metre is an instance of the class, but is a (particular) instance whose associated fields cannot be changed by other code. I cannot see how to achieve this. Any ideas please? t

+4  A: 

There are two ways to achieve something similar, depending on your needs:

The first way involves initialising an instance of your class either in a unit initialization procedure (remembering to Free the instance in the corresponding finalization procedure) or using an accessor unit function to initialise the instance on first access:

a)

  interface

  var
    metre : TUnit = NIL;  // Can initialise a unit variable

   :

  implementation

   :

  initialization
    metre := TUnit.Create( ... );

  finalization
    FreeAndNIL(metre);

  end.

NOTE: If your class is simply a container for data as it appears from your question then you might consider omitting the finalization Free'ing - although technicall a memory leak, you won't actually leak anything as the memory used by your instance will be reclaimed when your app process terminates. Free'ing in finalization might cause issues if other finalization code in your project accesses the metre instance after it has been free'd.

I'd also caution you against trying to use any so called "singleton" classes. They add complexity and overhead for zero benefit in such simple cases as this.

b)

  interface

    function Metre: TUnit;

  implementation

  var
    _metre : TUnit = NIL;  // Can initialise a unit variable


  function Metre: TUnit;
  begin
    if NOT Assigned(_metre) then
      _metre := TUnit.Create( ... );

    result := _metre;
  end;


  finalization
    FreeAndNIL(metre);

  end.

The same observations w.r.t the optionality of finalization apply in this case also.

Note also that if performance is a significant concern and this accessor function is likely to be used a lot then you can take further steps to optimise this area, using a function pointer initialised to a factory function initially, which replaces itself with a pointer to a simple unconditional accessor function upon first access.

The second way to achieve it is to use class functions to return fixed values from a specially derived class, rather than using member data. You don't then need to instantiate the class at all and do not need to worry about disposing of it either. I can't remember off the top of my head if Delphi 2010 now supports class variables as well as class functions - if it does then that may be an answer too. But if you aren't using Delphi 2010 then the question would be moot anyway.

It would look a little something like:

TMetre = class(TUnit)
  class function Name: String; override;
  class function Scale: Double; override;
end;

etc

I do wonder however... are you aware of the Standard Conversions unit and associated units of measure functionality? I only ask because the content in this question looks like it might be something that is already catered for by an existing aspect of the Delphi RTL.

Deltics
Many thanks for all that - very helpful. Re- conversions, yes I was vaguely aware of that, but I'm sketching out an ontology here rather than particularly interested in the unit-to-unit conversions themselves - they just pop up as part of teh ontological requirements.
+4  A: 

try readonly properties:

interface
type
  TUnit = class
  private
     _name: string;
     _scale: real;
     class function makeunit(aname: string; ascale: real): TUnit;
  public 
     property name: string read _name;
     property scale: real read _scale;
  end;

function metre: TUnit;

implementation

var
  _metre : TUnit = nil;

class function TUnit.makeunit(aname: string; ascale: real): Tunit;
begin
  Result._name := aname;
  Result._scale := ascale;
end;

function Metre: TUnit;
begin
  if not Assigned(_metre) then
    _metre := TUnit.makeunit('metre', 1.0);
  result := _metre;
end;

finalization
  FreeAndNIL(_metre);
Ozan
thanks - that seems to take care of the read-only requirementst
A: 

While it would be technically possible to create constant object values, that concept would be incompatible with normal memory deallocation methods, and Delphi/native does not support it.

Lars D