1. Computing

Delphi Question: Proper using of a Record Field/Property in a Class?

By November 29, 2006

Follow me on:

in POLLs :: Recently, a Delphi developer asked me why some of the source code he wrote using records and custom Delphi classes did not work (was producing a compile-time error).

Want to test your Delphi knowledge? Take a look at the following class declarations and code, then answer the poll question...

TInnerRecord = record
  Name : string;
end;

VERSION A) record as field
TTestClass = class
  InnerRecord : TInnerRecord;
end;

VERSION B) record as read-only property
TTestClass = class
private
  fInnerRecord : TInnerRecord;
public
  property InnerRecord : TInnerRecord read fInnerRecord;
end;

VERSION C) record as property
TTestClass = class
private
  fInnerRecord : TInnerRecord;
public
  property InnerRecord : TInnerRecord read fInnerRecord write fInnerRecord;
end;

Testing code...
var
  test : TTestClass;
begin
  test := TTestClass.Create;
  try
    test.InnerRecord.Name := 'Delphi';
  finally
    test.Free;
  end;
end;

Which of the class definitions WILL PRODUCE a Compile-Time ERROR?

Note: I'll reveal the correct answer in a few days. Make sure you sign up for the About Delphi Newsletter if you want to find out the correct answer and why there is a compile time error in (at least) one of them...

If you already voted, take a look at the poll results.

If you do not know the answer or you do know the answer but you are unsure in why is there an error, read the Troubleshoot "Left Side Cannot Be Assigned To" Compile Time Delphi Error article!

Related: Records in Delphi | OOP in Delphi | Delphi Polls

Comments
November 29, 2006 at 3:28 pm
(1) Tom Wilk says:

You have to use a POINTER to the record and allocate and deallocate memory for the record. It is like a struct in C.

See below.

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type

TInnerRecord = record
Name : string;
end;

PInnerRecord = ^TInnerRecord;

TTestClass1 = class
InnerRecord: PInnerRecord;
end;

TTestClass2 = class
private
FInnerRecord: PInnerRecord;
public
property InnerRecord: PInnerRecord read FInnerRecord;
end;

TTestClass3 = class
private
FInnerRecord: PInnerRecord;
public
constructor Create;
destructor Destroy; override;
property InnerRecord: PInnerRecord read FInnerRecord write FInnerRecord;
end;

TForm1 = class(TForm)
Class3: TButton;
Class2: TButton;
Class1: TButton;
procedure Class3Click(Sender: TObject);
procedure Class1Click(Sender: TObject);
procedure Class2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Class1Click(Sender: TObject);
var
test1: TTestClass1;
begin
test1 := TTestClass1.Create;
try
GetMem(test1.InnerRecord, SizeOf(TInnerRecord));
test1.InnerRecord.Name := ‘Delphi’;
finally
FreeMem(test1.InnerRecord);
test1.Free;
end;
end;

procedure TForm1.Class2Click(Sender: TObject);
var
test2: TTestClass2;
begin
test2 := TTestClass2.Create;
try
GetMem(test2.FInnerRecord, SizeOf(TInnerRecord));
test2.InnerRecord.Name := ‘Delphi’;
finally
test2.Free;
end;
end;

procedure TForm1.Class3Click(Sender: TObject);
var
test3: TTestClass3;
begin
test3 := TTestClass3.Create;
try
test3.InnerRecord.Name := ‘Delphi’;
finally
test3.Free;
end;
end;

{ TTestClass3 }

constructor TTestClass3.Create;
begin
inherited Create;
GetMem(FInnerRecord, SizeOf(TInnerRecord));
end;

destructor TTestClass3.Destroy;
begin
FreeMem(FInnerRecord);
inherited Destroy;
end;

end.

November 29, 2006 at 7:59 pm
(2) gp says:

Tom, the sure thing is that I’d never, ever, hire you.

November 30, 2006 at 3:56 am
(3) Nico says:

The “suprise” shouldn’t be. Properties are “syntactic sugar” for a “setter”. Even if the “write” clause refers to a simple field, the compiler produces a method that will take as a “Value” parameter the property type as a whole, not only one of its fields if it’s a complex type.

November 30, 2006 at 9:53 am
(4) Tom Wilk says:

GP, Let’s see your answer Einstien. ;>)

November 30, 2006 at 7:20 pm
(5) gp says:

Well Tom, it doesn’t take to be Einstein to give a simpler answer. Hint: you don’t need pointers – at all. Just try all answers. Incidentally, there’s currently a thread about engineers. I think you belong to the “Rube Goldberg” type, i.e.: “Creates incredibly complex solutions to simple problems. This is often due to a lack of experience or a resistance to research.” Or maybe a messy brain :)

November 30, 2006 at 7:51 pm
(6) Tom Wilk says:

GP,

For me, the simplest solution is the one that a) works and b) the one I am most familiar with. I am not afraid of using pointers. This solution does work under Delphi 6. There maybe other answers to this such as using a local var to allocate memory for the record. I just choose to use GetMem and FreeMem instead. Since I am working in Delphi 6, I do not have all the latest record type compiler features found in Delphi 2006. Please educate us all and post the “OTHER” solution(s) you are thinking of. :>)

November 30, 2006 at 10:09 pm
(7) Petra Eloii says:

I am amazed that so many programmers get this simple quiz wrong.

This is the result of:

1. Low IQ types entering the industry
and
2. People without a computer science background trying to pass themselves off as programmers.

This is why so much software is so buggy.

December 1, 2006 at 8:09 am
(8) Bjoern says:

@Tom: There is no need for a “solution” in the sense of what you posted. The “solution” is the correct answer to the question which definition(s) produce compile-time error(s). Your code may be the kind of code you’re most familiar with, but it’s definitley not the simplest solution that works. I agree with GP, I’d never hire you. And that has nothing to do with being afraid of pointers or the latest record type features in newer delphi features. Try thinking more in OO and not in C, at least if you code in OO.

December 6, 2006 at 10:03 pm
(9) Ed says:

.
//Testing code… Version (c)
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Test : TTestClass;
begin
Test := TTestClass.Create;
try
// Test.InnerRecord.Name := ‘Delphi Compiler Test’; //replace this line
Test.fInnerRecord.Name := ‘Delphi Compiler Test’; //with this one
finally
begin
Label1.Caption := Test.fInnerRecord.Name;
Label2.Caption := Test.InnerRecord.Name;
Test.Free;
end;
end;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Label1.Caption := ”;
Label2.Caption := ”;
end;
.

Can someone tell me why the above is wrong? It compiles OK in Delphi 7.

Thanks for a great discussion.

Regards,

Ed.

December 7, 2006 at 3:00 am
(10) Zarko Gajic says:

Ed, you are accessing a private field “fInnerRecord” – that would not be possible if the class is in another unit.

Leave a Comment

Line and paragraph breaks are automatic. Some HTML allowed: <a href="" title="">, <b>, <i>, <strike>

©2014 About.com. All rights reserved.