我遇到这种情况:
type
X1 = Class
protected
oMyList: TMyList;
public
property MyList: TMyList write oMyList;
end;
X2 = Class(X1)
public
procedure GetMyList;
end;
使用:
procedure X2.GetMyList;
begin
Writeln (oMyList.Count); // <-- Return exception
end;
并在主程序中:
var
P: X2;
MyList: TMyList;
begin
P := X2.Create;
try
P.MyList := MyList;
P.GetMyList;
finally
P.Free;
end;
end;
当我尝试阅读oMyList.Count
时,问题是个例外。当然,MyList
是正确创建和定义的。
我的错误在哪里?
我遇到这种情况:
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.
所有工作都很完美,仅在TX2.Create
我尝试阅读oDBEstr
时有例外。当然,我以TX2.Create
为例,但我需要访问所有TX2中的oDBEstr
,而不仅仅是TX2.Create
。
答案 0 :(得分:3)
注意:此答案涉及原始问题。
尚未在标有主程序的代码部分中创建局部变量MyList
。因此,对象引用将具有来自堆栈的一些垃圾值。当您第一次访问该列表时,会发生访问冲突。
答案 1 :(得分:0)
注意:此答案涉及更新的问题。
在更新的代码中,您只需在访问之前不创建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
对象引用的空操作,这在编码析构函数时非常方便。