有没有办法在Delphi 7中获取旧款(Borland Pascal)对象实例的类名?

5

我有很多我的类的子类:

PMyAncestor =^TMyAncestor;
TMyAncestor = object
  public
    constructor init;
    destructor done; virtual;
    // There are virtual methods as well
end;

PMyDescendant1 =^TMyDescendant1;
TMyDescendant1 = object ( TMyAncestor )
end;

PMyDescendant2 =^TMyDescendant2;
TMyDescendant2 = object ( TMyAncestor )
end;

PMyDescendant3 =^TMyDescendant3;
TMyDescendant3 = object ( TMyDescendant2 )
end;

procedure foo;
var
  pMA1, pMA2, pMA3, pMA4 : PMyAncestor;
  s : string;
begin
  pMA1 := new( PMyAncestor, init );
  pMA2 := new( PMyDescendant1, init );
  pMA3 := new( PMyDescendant2, init );
  pMA4 := new( PMyDescendant3, init );
  try
    s := some_magic( pMA1 ); // s := "TMyAncestor"
    s := some_magic( pMA2 ); // s := "TMyDescendant1"
    s := some_magic( pMA3 ); // s := "TMyDescendant2"
    s := some_magic( pMA4 ); // s := "TMyDescendant3"
  finally
    dispose( pMA4, done );
    dispose( pMA3, done );
    dispose( pMA2, done );
    dispose( pMA1, done );
  end;
end;

有没有办法获取其后代实例的类名?我不想为此创建虚拟方法(因为有数千个后代)。我知道有 typeOf(T) 运算符。但它的返回类型是什么?好的,指针。但我应该将它转换成什么?转换为 PTypeInfo 似乎是错误的。

@Dsm ClassName是TObject后代的一种方法。不幸的是,我的基类是更早的Borland Pascal基础类型,而不是这个(由新的释放操作符创建)。 - User007
我想你也知道,你试图做的事情是不可能的。 - David Heffernan
@DavidHeffernan 是的,这是我的想法。但我相信肯定有其他方法。看来我必须创建那个虚拟方法!:( 你有什么想法typeOf应该返回什么类型? - User007
1
你唯一可以使用TypeOf的方式就是跟一个指定类型进行比较:if TypeOf(pMA1^) = TypeOf(TMyAncestor) then ... - LU RD
我并不是在告诉你这很容易,只是说这是可能的。 - LU RD
显示剩余6条评论
3个回答

5
当我编译这段代码并在可执行文件中搜索你的类名时,发现它们没有被找到。因此,我得出结论,你尝试做的事情是不可能的。

我也很害怕那个 :( - User007
1
通过与 TypeOf 进行比较,可以获取对象类型的正确名称。请参阅我对问题的评论。 - LU RD
2
在这个问题中,很明显不知道如何使用TypeOf。OP要求一个神奇的函数来揭示对象类型。我认为TypeOf正是那个神奇的函数,只是它返回定义类型的指针,而不是字符串。是否需要使用查找表,这是实现细节,我们无法确定。 - LU RD
TypeOf 的结果可以像对象名称一样使用。 - LU RD
@LURD 如果您想向用户呈现文本,那么不是这样的。我的意思是,也许提问者有自己的原因要求文本。或者,类型身份才是重要的,而不是文本值。但问题确实要求文本。 - David Heffernan
显示剩余5条评论

4

无法获取旧式对象类型名称。

使用 TypeOf(),可以测试对象是否等于某种类型:

if TypeOf(pMA1^) = TypeOf(TMyAncestor) then ...

它还可以用于构建查找表,以便与实际类型名称匹配。如果需要记录许多对象类型到这样的表中,则可能会有点繁琐。
在评论中,有人说它将用于通过在基本对象初始化/完成期间记录名称来捕获内存泄漏。
以下是一个示例,它记录日志,但不是类型名称,而是记录类型名称地址。它还打印基本对象的名称和地址,这对于确定泄漏很有用。对象地址按声明顺序编号,使用此信息应该很容易识别泄漏对象。
program Project121;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

Type
  PMyAncestor =^TMyAncestor;
  TMyAncestor = object
   public
    constructor init;
    destructor done; virtual;
    // There are virtual methods as well
  end;

  PMyDescendant1 =^TMyDescendant1;
  TMyDescendant1 = object ( TMyAncestor )
  end;

  PMyDescendant2 =^TMyDescendant2;
  TMyDescendant2 = object ( TMyAncestor )
  end;

  PMyDescendant3 =^TMyDescendant3;
  TMyDescendant3 = object ( TMyDescendant2 )
  end;

constructor TMyAncestor.init;
begin
{$IFDEF DEBUG}
  WriteLn( IntToHex(Integer(TypeOf(Self))),
           ' Base class - TMyAncestor:',
           IntToHex(Integer(TypeOf(TMyAncestor))));
{$ENDIF}
end;

destructor TMyAncestor.done;
begin
{$IFDEF DEBUG}
  WriteLn(IntToHex(Integer(TypeOf(Self))),' Done.');
{$ENDIF}
end;

procedure foo;
var
  pMA1, pMA2, pMA3, pMA4 : PMyAncestor;
  s : string;
begin
  pMA1 := new( PMyAncestor, init );
  pMA2 := new( PMyDescendant1, init );
  pMA3 := new( PMyDescendant2, init );
  pMA4 := new( PMyDescendant3, init );
  try
    (*
      Do something
    *)
  finally
    dispose( pMA4, done );
    dispose( pMA3, done );
    dispose( pMA2, done );
    dispose( pMA1, done );
  end;
end;

begin
  foo;
  ReadLn;
end.

输出:

0041AD98 Base class - TMyAncestor:0041AD98
0041ADA8 Base class - TMyAncestor:0041AD98
0041ADB8 Base class - TMyAncestor:0041AD98
0041ADC8 Base class - TMyAncestor:0041AD98
0041ADC8 Done.
0041ADB8 Done.
0041ADA8 Done.
0041AD98 Done.

类型指针对我来说没用。我记录实例指针。另外,添加一个日志条目计数器(分别创建/销毁)以便在重新运行“测试用例”时能够捕获它。需要类名以使日志更易于人类追踪。我已经创建了不喜欢的虚拟方法,并在最重要的类型中覆盖了它。其他的写默认值:Object。将来我会覆盖那些变得重要的方法。但如果可能的话,我想避免这样做。 - User007

0

这是 Delphi 文档关于对象类型的说明

Delphi 编译器允许使用另一种语法来声明类类型。您可以使用以下语法声明对象类型:

type objectTypeName = object (ancestorObjectType)
         memberList
      end;

其中,objectTypeName 是任何有效的标识符,(ancestorObjectType) 是可选的,memberList 声明字段、方法和属性。如果省略 (ancestorObjectType),则新类型没有祖先。对象类型不能有发布成员。
由于对象类型不是从 System.TObject 继承而来,因此它们不提供内置的构造函数、析构函数或其他方法。您可以使用 New 过程创建对象类型的实例,并使用 Dispose 过程销毁它们,或者您可以像处理记录一样声明对象类型的变量。
对象类型仅支持向后兼容。不建议使用它们。
所以你的问题的答案是“否”。
你使用的对象类型不包含检索类名所需的必要方法,正常继承自 TObject 的类都有这个方法。
现在你该怎么办呢?
你可以修改现有的对象,添加额外的数据字段,在其中存储它们的名称,并在需要时读取这个名称。你将不得不自己在对象创建时设置这个值。

或者,您可以将所有对象替换为从TObject继承的真正类,以便自动获取所有类功能。尽管在某些情况下,对象类型可能具有一些优势,但这通常是更推荐的方法。


这是一个关于史前对象类型的不错解释,但并非问题的答案。你推荐的解决方案是问题的一部分,而不是新的想法。它期望有一个getName方法。为每个后代创建虚拟方法或逐个修改它们的构造函数是相同的。 - User007
@User007 就我所看到的,这个回答确实回答了你的问题。“所以,你的问题的答案是否定的。” - Graymatter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接