Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: Record constructor vs factory function

So what will be the preferred way of initializing records?

With a 'factory function':

TMyRecord = record
  valueX: integer;
  valueY: integer;
end;

function MyRecord(const AValueX, AValueY: integer): TMyRecord;
begin
  result.valueX := AValueX;
  result.valueY := AValueY;
end;

var
  myrec: TMyRecord;
begin
  myrec := MyRecord(1, 2);
end;

or a constructor:

TMyRecord = record
  valueX: integer;
  valueY: integer;
  constructor Create(const AValueX, AValueY: integer);
end;

constructor TMyRecord.Create(const AValueX, AValueY: integer);
begin
  self.valueX := AValueX;
  self.valueY := AValueY;
end;

var
  myrec: TMyRecord;
begin
  myrec := TMyRecord.Create(1, 2);
end;

I feel that the constructor things more encapsulated, but it makes it easy to get confused when reading code. It makes it look like a class that lack a call to free. It's also more to type...

Why would you prefer one over the other?

like image 291
Vegar Avatar asked Jul 12 '09 19:07

Vegar


3 Answers

I prefer classes, but if I have to use records, I like to treat them as similar as classes as possible. So I use the record constructor.

But there is an annoying bug with records and units. If a function returns a record (with methods), it produces an internal error if you want to access these methods. You can circumvent this by assigning it to another variable:

type 
  TMyRec = record
    ..
    procedure X;
  end;


function GetRec: TMyRec;



procedure Test;
var
  r1, r2 : TMyRec;
begin
  r1 := GetRec;
  r1.X; // internal error
  r2 := r1;
  r2.X; // No internal error;
like image 174
Toon Krijthe Avatar answered Nov 05 '22 16:11

Toon Krijthe


I'd prefer "factory method" like

  function TMyRecord.CreateRec(const AValueX, AValueY: integer): TMyRecord;

Separate factory function leaks incapsulation and record constructors just confuse IMHO.

like image 33
Fr0sT Avatar answered Nov 05 '22 15:11

Fr0sT


In a Delphi project that I created, I used records instead of classes to reduce the amount of overhead on a list. I would have several hundreds of records in a dynamic array so I created two records. The first record was for the item itself. The fields were made private (yes, you can use private/protected with records) and added read-only properties to the public section. An additional constructor was also added to initialize the record in the correct way. This setup allowed me to protect the contents from this record from other developers. The second record was just a wrapper around a dynamic array of the previous record type. The array would be private and I added methods to get, add and delete records in this list. As a result, the whole list is protected against mis-use by other developers and also has a lot less overhead than a regular TList/TObjectList solution.

Do keep in mind that records aren't classes. You can't inherit constructors and other methods. They have less functionality than true classes in WIN32 environments. In .NET, they're just promoted to classes again. And it's not very useful to use add a constructor when developers can easily modify the contents of each and every field in your record. You should use constructors to protect those fields instead.

like image 1
Wim ten Brink Avatar answered Nov 05 '22 16:11

Wim ten Brink