尝试读取时继承了list和exception的类

时间:2011-12-08 14:56:10

标签: delphi delphi-xe2

原始问题

我遇到这种情况:

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

2 个答案:

答案 0 :(得分:3)

注意:此答案涉及原始问题。

尚未在标有主程序的代码部分中创建局部变量MyList。因此,对象引用将具有来自堆栈的一些垃圾值。当您第一次访问该列表时,会发生访问冲突。

答案 1 :(得分:0)

注意:此答案涉及更新的问题。

在更新的代码中,您只需在访问之前不创建oDBEstrTX2.Create的构造函数不创建它,TX2派生自的类没有构造函数。因此,自然会发生访问冲突。

这些类的意图似乎是TX1,因此TX2引用保存到拥有的DBEstr 由不同的班级。您已实现了允许TX1TX2的用户设置此引用的读/写属性。但是,在构造函数完成运行之前,它们不能这样做。 TX1TX2的用户只有在引用TX1TX2后才能设置该属性。因此,要解决这个难题,您唯一的选择是将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对象引用的空操作,这在编码析构函数时非常方便。