Delphi根据TObject类型调用虚构造函数

Delphi calling a virtual constructor based on the TObject type

我有一个派生自 TStringList 对象的对象,我称之为“TAutoString”。它允许您在创建列表时指定对象类型。然后,每次将新条目添加到字符串列表时,它还会创建与该字符串条目关联的对象的副本。这使得将各种附加信息与每个字符串一起存储起来变得容易。例如:

type TMyObject = class(TObject)
 public
  Cats: integer;
  Dogs: integer;
  Mice: integer;
 end;

MO := TAutoString.Create(TMyObject);

在对象内部,class 信息存储在一个 class 变量中:

 private
  ObjectClass: TClass;


constructor TAutoString.Create(ObjClass: TClass);
begin
    inherited Create;
    ObjectClass:=ObjClass;
end;

现在,每次添加新项目时,都会创建一个指定类型的新对象:

function TAutoString.Add(const S: string): Integer;
begin
    Result:=inherited Add(S);
    Objects[Result]:=ObjectClass.Create;
end;

我现在可以添加或读取与每个字符串条目关联的信息。

   TMyObject(MO.Objects[25]).Cats := 17;
   D:=TMyObject(MO.Objects[25]).Dogs;

只要对象没有构造函数,它就可以很好地工作。如果对象有构造函数,则在创建对象时不会调用其构造函数,因为 TObject 的构造函数不是虚拟的。

谁能想出解决这个问题的方法。我见过使用 RTTI 库的解决方案,但这是在 Delphi-7 中,它没有 RTTI 库。

顺便说一句,TObject 的构造函数不是虚拟的似乎有点奇怪。如果是,它将启用各种有用的功能,例如我正在尝试实现的功能。

编辑: Remy 下面的建议正是我所需要的推动力。我最初尝试过类似的策略,但我无法让它发挥作用。当它似乎不像我认为的那样工作时,我认为一定有一些我不了解虚拟方法的地方。他的 post 促使我再看一遍。事实证明,我已经放弃了我想要附加的对象的构造函数的“Override”指令。现在一切正常。

我担心的另一个问题是我已经在一堆其他应用程序中使用了自动字符串,其中对象基于“TObject”,我不想回去更改所有代码.我通过重载构造函数并为基于 TObject 的对象和另一个我的 TAutoClass 对象设置一个构造函数来解决这个问题:

  constructor Create(ObjClass: TAutoClass); overload; virtual;
  constructor Create(ObjClass: TClass); overload; virtual;

根据调用的构造函数,对象class存储在不同的变量中。

 private
  AutoClass: TAutoClass;
  ObjectClass: TClass;

然后当构造对象时,我检查哪个已被分配并使用那个:

procedure TAutoString.CreateClassInstance(Index: integer);
begin
   if AutoClass<>nil then Objects[Index]:=AutoClass.Create
   else Objects[Index]:=ObjectClass.Create
end;

新版本与任何一种类型的对象都完美兼容。

要执行您想要的操作,您必须为要从中派生的列表对象定义一个基 class,然后您可以向该 class 添加一个虚拟构造函数。您的 ObjectClass 成员必须使用 class 类型,而不是使用 TClass.

例如:

type
  TAutoStringObject = class(TObject)
  public
    constructor Create; virtual;
  end;

  TAutoStringObjectClass = class of TAutoStringObject;

  TAutoString = class(TStringList)
  private
    ObjectClass: TAutoStringObjectClass;
  public
    constructor Create(ObjClass: TAutoStringObjectClass);
    function Add(const S: string): Integer; override;
    ...
  end;

...

constructor TAutoStringObject.Create;
begin
  inherited Create;
end;

constructor TAutoString.Create(ObjClass: TAutoStringObjectClass);
begin
  inherited Create;
  ObjectClass := ObjClass;
end;

function TAutoString.Add(const S: string): Integer;
var
  Obj: TAutoStringObject;
begin
  Obj := ObjectClass.Create;
  try
    Result := inherited AddObject(S, Obj);
  except
    Obj.Free;
    raise;
  end;
end;

...

然后,您只需调整派生对象 classes 以使用 TAutoStringObject 而不是 TObject,例如:

type
  TMyObject = class(TAutoStringObject)
  public
    ...
    constructor Create; override;
  end;

MO := TAutoString.Create(TMyObject);
...

他们的构造函数将如预期的那样被调用。

这里有一个更简洁的解决方案的提示:

可以向 Tobject 添加虚构造函数。

为此,您需要使用所谓的“class 助手”。

这是一个例子:

type
    TobjectHelper = class helper for Tobject
    public
        constructor Create; virtual; // adds a virtual constructor to Tobject.
    end;

(我的用例是一个 TryCreateObject 函数,用于检测对象创建期间内存不足的情况,包装 try except end 并简单地 returns true/false 以防止代码中的 try except 块,而是使用更多逻辑可控的 if 语句)

Class 助手显然(?)在 Delphi 8 及更高版本中引入。您的要求是 Delphi 7,所以这可能行不通。

除非您为 Windows 95/Windows 98/Windows XP 编写代码,否则您可能是时候升级到更新版本的 Delphi,尤其是Delphi 支持 unicode 的 XE 版本,否则你将针对即将过时的老化平台进行编码等

但是对于 Windows 95/Windows 98 和 Windows XP 我相信 Delphi 2007 可能会有一些用处,我相信它可以编译代码可以运行 在那些较旧的 windows 平台上,我可能是错的。

Delphi 的更高版本要求某些 windows 系统 DLL 存在,否则 build/compiled 可执行文件将不会 运行、w95/w98/wxp 缺少这些 dll。