Примечание: Этот ответ относится к обновленному вопросу.
В своем обновленном коде вы просто не создаете oDBEstr
, прежде чем получить к нему доступ.Конструктор TX2.Create
не создает его, а класс, из которого получен TX2
, не имеет конструктора.Таким образом, естественно происходит нарушение доступа.
Намерение этих классов, по-видимому, состоит в том, что TX1
и, следовательно, TX2
содержит ссылку на DBEstr
, которая принадлежит другим классом.Вы реализовали свойство чтения / записи, которое позволяет пользователям TX1
и TX2
устанавливать эту ссылку.Тем не менее, они не могут сделать это, пока конструктор не закончил работу.Пользователи TX1
и TX2
могут установить это свойство, только если у них есть ссылка на TX1
или TX2
.Таким образом, для решения загадки ваш единственный вариант - передать экземпляр DBEstr
конструктору TXE2
в качестве параметра.
Вот рабочая версия вашего кода, которая иллюстрирует, как это сделать:
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 Destroy; override;
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.Destroy;
begin
oDBEstr.Free;
inherited;
end;
// Module 2
type
TX0 = class
protected
oArchive: TArchive;
function GetDBEstr: TDBEstr;
public
constructor Create;
destructor Destroy; override;
property DBEstr: TDBEstr read GetDBEstr;
end;
constructor TX0.Create;
begin
oArchive := TArchive.Create;
end;
destructor TX0.Destroy;
begin
oArchive.Free;
inherited;
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(ADBEstr: TDBEstr);
end;
constructor TX2.Create(ADBEstr: TDBEstr);
begin
inherited Create;
oDBEstr := ADBEStr;
writeln(oDBEstr.Count);
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(X0.DBEstr);
try
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.
Я понимаю, что этот код является иллюстративным примером, взятым из вашего реального кода, но я сомневаюсь в использованиисвойство чтения / записи для TX1.DBEstr
.Я подозреваю, что каждый экземпляр TX1
будет использовать один и тот же экземпляр DBEstr
в течение всего времени существования экземпляра TX1
.Если это так, то он должен быть передан конструктору в TX1
, и свойство будет доступно только для чтения:
type
TX1 = class
var
oDBEstr: TDBEstr;
public
constructor Create(ADBEstr: TDBEstr);
property DBEstr: TDBEstr read oDBEstr;
procedure Load;
end;
constructor TX1.Create(ADBEstr: TDBEstr);
begin
inherited Create;
oDBEstr := ADBEStr;
end;
Это изменение приведет к следующей реализации для TX2
type
TX2 = class(TX1)
public
constructor Create(ADBEstr: TDBEstr);
end;
constructor TX2.Create(ADBEstr: TDBEstr);
begin
inherited;
writeln(oDBEstr.Count);
end;
Обратите внимание, что я исправил деструкторы в вашем коде.Деструкторы всегда должны называться Destroy
, они всегда должны быть объявлены с ключевым словом override
, и они всегда должны вызывать inherited
в качестве последнего действия.Обратите внимание, что вы никогда не звоните Destroy
напрямую, а звоните Free
.Это соглашение должно разрешить Free
быть нулевой операцией для неинициализированной ссылки на объект nil
, что очень удобно при кодировании деструкторов.