【问题标题】:How to check if a Delphi class is declared abstract?如何检查 Delphi 类是否被声明为抽象类?
【发布时间】:2010-10-25 13:06:35
【问题描述】:

在 Delphi 中是否可以使用 RTTI(或其他东西)来检查一个类是否被声明为抽象类? 比如:

TMyAbstractClass = class abstract(TObject)
  // ...
end;

...

if IsAbstract(TMyAbstractClass.ClassInfo) then
  ShowMessage('Yeah')
else
  ShowMessage('Computer says no...');

【问题讨论】:

标签: delphi rtti


【解决方案1】:

我没有足够新的版本来直接回答您的问题,但请记住,是否是抽象的并不真正重要。所做的只是让编译器阻止你直接在类上调用构造函数。如果您将类引用放入类引用变量中,编译器将允许您调用该变量的构造函数,并且在运行时您将拥有一个本应不可实例化的类的实例。

var
  c: TClass;
  o: TObject;
begin
  c := TMyAbstractClass;
  o := c.Create;
  Assert(o is TMyAbstractClass);
end;

真正重要的是类是否有抽象方法。你可以很容易地检查。查看班级的 VMT。任何包含指向System._AbstractError 的指针的虚方法槽都是抽象方法。棘手的部分是知道要检查多少虚拟方法槽,因为没有记录。 Allen Bauer demonstrated how to do that 在对 another question 的回答中,但在 cmets 中,Mason Wheeler 指出它可能会返回比应有的更大的值。他提到了JCL 中的GetVirtualMethodCount 函数,它应该可以更准确地计算用户定义的虚拟方法。使用该函数和同样来自 JCL 的 GetVirtualMethod,我们得到了这个函数:

function HasAbstractMethods(c: TClass): Boolean;
var
  i: Integer;
begin
  Result := True;
  for i := 0 to Pred(GetVirtualMethodCount(c)) do
    if GetVirtualMethod(c, i) = @_AbstractError then
      exit;
  Result := False;
end;

如果一个抽象类没有抽象方法,那么它到底能有多抽象呢?它必须被标记为抽象以防止开发人员创建它的实例,但如果你真的想要,无论如何你都可以创建它的实例,因此标记抽象类实际上更像是一个警告,而不是任何实际的使用限制。

【讨论】:

  • 不幸的是,当我进行一些测试时,Allen 的算法在所有情况下都不起作用。如果我能弄明白,我会稍微研究一下,并在该问题中进行更正。
  • 显然一些其他的东西可以放在名字前面。尝试使用 JclSysUtils.GetVirtualMethodCount 代替 Allen 的算法,它与 GetVirtualMethod 位于同一 JCL 单元中,非常方便。
  • 确实如此。 JCL 版本检查 VMT 中的所有其他指针,如果它们中的任何一个出现在第一个虚拟方法和类名之间,它就会“缩短”它认为是虚拟方法范围的窗口。
【解决方案2】:

快速浏览 TypInfo 单元并没有发现任何有用的信息。我认为“抽象类”的概念纯粹是为了编译器的利益。它给了它一个强制执行的规则——没有这个类的实例化,只有它的后代——但在运行时并没有真正做任何事情,所以没有必要为它记录任何 RTTI。

出于好奇,您为什么还要尝试找出这个问题?

【讨论】:

  • 我有一个 TCollection 派生类,它可以包含具有共同祖先的不同类类型 (TCollectionItem)。具体来说,它是一个包含不同类型列的网格列集合。现在我想创建一个设计时集合编辑器,允许用户添加注册的列类型(使用 TClassFinder).. 但我不希望这个编辑器中的抽象类。我的抽象类被命名为 TCustom* 所以现在我使用类名进行过滤。
  • 因此,您的问题不在于如何检测抽象类。您的问题是如何获取有效“列类型”的列表。简单的解决方案是确保只注册有效列类型的类。首先不要注册抽象类。注册一个类的唯一原因是您可以按名称搜索它,然后实例化它。不要注册永远不会被实例化的类。您应该会发现 VCL 也没有注册自己的“自定义”基类。
  • 是的,非常正确,问题是我只注册了“有效”的类,正如你所建议的那样,但 Delphi 也会自动注册父类,例如如果您使用 RegisterClass(TButton) 注册 TButton,Delphi 也会注册 TCustomButton、TButtonControl 等...无论如何感谢您关于抽象方法的帖子,非常有趣,但我想我的问题的正确答案是“否”跨度>
  • "[“抽象类”的概念]给[编译器]一个强制执行的规则——没有这个类的实例化,只有它的后代”——没有Delphi 中存在规则。编译器将愉快地实例化一个抽象类,并且在运行时实际调用其抽象方法之一之前不会发生错误。我见过很多次人们不小心直接实例化了TStringsTStream,然后想知道为什么他们的代码会因此而崩溃。
猜你喜欢
  • 1970-01-01
  • 2014-02-26
  • 2021-06-10
  • 2015-08-25
  • 1970-01-01
  • 2016-05-28
  • 2016-05-10
  • 2020-04-12
  • 1970-01-01
相关资源
最近更新 更多