Original Question
I have this situation:
type
X1 = Class
protected
oMyList: TMyList;
public
property MyList: TMyList write oMyList;
end;
X2 = Class(X1)
public
procedure GetMyList;
end;
with:
procedure X2.GetMyList;
begin
Writeln (oMyList.Count); // <-- Return exception
end;
and in main program:
var
P: X2;
MyList: TMyList;
begin
P := X2.Create;
try
P.MyList := MyList;
P.GetMyList;
finally
P.Free;
end;
end;
The problem is an exception when I try to read oMyList.Count. Of course, MyList is created and defined correctly.
Where is my mistake?
Updated Question
I have this situation:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Generics.Collections;
// Module 1
type
PDBEstr = Integer; // Just an example of list with integer
TDBEstr = TList<PDBEstr>;
TArchive = class
protected
oDBEstr: TDBEstr;
public
constructor Create;
destructor Free;
property DBEstr: TDBEstr read oDBEstr;
end;
constructor TArchive.Create;
begin
oDBEstr := TList<PDBEstr>.Create;
oDBEstr.Add(36); // Add an element to list
end;
destructor TArchive.Free;
begin
oDBEstr.Free;
end;
// Module 2
type
TX0 = class
protected
oArchive: TArchive;
function GetDBEstr: TDBEstr;
public
constructor Create;
destructor Free;
property DBEstr: TDBEstr read GetDBEstr;
end;
constructor TX0.Create;
begin
oArchive := TArchive.Create;
end;
destructor TX0.Free;
begin
oArchive.Free;
end;
function TX0.GetDBEstr: TDBEstr;
begin
Result := oArchive.DBEstr;
end;
// Module 3
type
TX1 = class
var
oDBEstr: TDBEstr;
public
property DBEstr: TDBEstr read oDBEstr write oDBEstr;
procedure Load;
end;
procedure TX1.Load;
begin
writeln (oDBEstr.Count); // Return 1
end;
// Module 4
type
TX2 = class(TX1)
public
constructor Create;
end;
constructor TX2.Create;
begin
inherited;
// In this point i need to have access to oDBEstr for work with data
// in oDBEstr
writeln(oDBEstr.Count); // <----- Return Exception
end;
// Main Program
var
X0: TX0;
X2: TX2;
begin
try
X0 := TX0.Create;
try
writeln(X0.DBEstr.Count); // Return 1
writeln(X0.DBEstr.First); // Return 36
X2 := TX2.Create;
try
X2.DBEstr := X0.DBEstr;
writeln(X2.DBEstr.count); // Return 1
finally
X2.Free;
end;
finally
X0.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
All work perfectly, only in TX2.Create I have exception when I try to read oDBEstr. Of course, I take as example TX2.Create, but I need to have access to oDBEstr in all TX2, not only in TX2.Create.
Note: This answer refers to the updated question.
In your updated code you simply do not create
oDBEstrbefore you access it. The constructor ofTX2.Createdoes not create it, and the class thatTX2is derived from does not have a constructor. So naturally an access violation occurs.The intent of these classes appears to be that
TX1and henceTX2hold a reference to aDBEstrthat is owned by a different class. You have implemented a read/write property that allows users ofTX1andTX2to set this reference. However, they can’t do so until the constructor has finished running. The users ofTX1andTX2can only set that property once they have a reference to aTX1orTX2. So, to solve the conundrum your only option is to pass theDBEstrinstance to the constructor ofTXE2as a parameter.Here’s a working version of your code this illustrates how to do this:
I realise that this code is an illustrative example culled from your real code but I question the use of a read/write property for
TX1.DBEstr. I suspect that each instance ofTX1will use the sameDBEstrinstance for the entire lifetime of theTX1instance. If so then it should be passed in to a constructor onTX1and the property made read-only:This change would lead to the following implementation for
TX2Note as an aside that I have fixed the destructors in your code. Destructors should always be called
Destroy, they should always be declared with theoverridekeyword, and the should always callinheritedas their final action. Note that you never callDestroydirectly, rather you callFree. This convention is to enableFreeto be a null operation on an uninitializednilobject reference which is a great convenience when coding destructors.