1

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

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

MO := TAutoString.Create(TMyObject);

在对象内部,类信息存储在类变量中:

 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 的构造函数不是虚拟的似乎有点奇怪。如果是这样,它将启用各种有用的功能,例如我正在尝试实现的功能。

编辑:雷米的建议只是我需要的轻推。我最初尝试过类似的策略,但我无法让它发挥作用。当它看起来不像我认为的那样工作时,我认为一定有一些我不了解虚拟方法的地方。他的帖子促使我再看一遍。事实证明,我已经为要附加的对象的构造函数放弃了“覆盖”指令。现在它按应有的方式工作。

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

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

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

 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;

新版本完美地适用于任何一种类型的对象。

4

2 回答 2

3

为了做你想做的事,你必须为你的列表对象定义一个基类来派生,然后你可以向那个类添加一个虚拟构造函数。您的ObjectClass成员将不得不使用该类类型而不是使用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;

...

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

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

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

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

于 2020-09-11T20:54:08.973 回答
0

以下是更清洁解决方案的提示:

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

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

这是一个例子:

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

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

Delphi 8 及更高版本中引入了类助手(?)。您的要求是针对 Delphi 7,所以这可能行不通。

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

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

更高版本的 Delphi 需要存在某些 Windows 系统 DLL,否则构建/编译的可执行文件将无法运行,w95/w98/wxp 缺少这些 dll。

于 2021-10-23T10:26:26.120 回答