如何确定一个 Delphi 对象是否属于特定类而不是任何派生类?

24

我有这些类和一个过程:

 TParent = class(TObject);
 TChild1 = class(TParent);     
 TChild2 = class(TParent);

 Procedure DoSomething(obj:TParent);

我想要做的是,当obj是一个TParent并且不是后代时抛出异常。

我考虑了像这样做:

if obj.classname = TParent.classname then raise exception.create....

但它似乎有点hackish(TM)

更多信息:我的意图是能够传递具有共同属性/过程的对象。经过更多思考,TParent对象并不是真正需要的,我需要的是我在答案中展示的接口对象。


除了已经给出的答案之外,确实存在一些合理的情况需要知道这个,但并不是很多。你为什么需要知道呢? - Lieven Keersmaekers
4
+1 表示你认为那是一个黑客行为。一般来说,如果你在处理字符串相关的事情,那么你可能做错了什么。 - Rob Kennedy
6个回答

40
您可能会发现以下TObject类方法很有用:
  • ClassType - 返回一个对象的类
  • ClassParent - 给出类的父类
  • InheritsFrom - 返回一个类是否继承自另一个类(即检查整个继承链)。 它包括当前类。

因此,您可以使用以下代码实现所需功能(下降自TParent但不是TDescendant?)(未经测试,此时没有Delphi):

if obj.ClassType.InheritsFrom(TParent)
  and not obj.ClassType.InheritsFrom(TDescendant) then...

或者,如果我误解了您的意思,您只想查看一个对象是否是TParent,而不是任何后代类型,请尝试:

if obj.ClassType = TParent then...

通过提供对元类的访问,Delphi走在了时代的前沿,因此您可以访问实际的类对象,而不仅仅是检查类名。


2
也许是领先时代,但仍然远远落后于Smalltalk! - David Heffernan
是的,我很想在我的回答中添加一个Smalltalk注释 :) - David

17

你走在正确的道路上,但是不需要比较类名,检查ClassType属性会更简单。

if obj.ClassType = TParent then raise exception.create....

5
不仅要更简单,还要更正确。两个类可以有相同的名称但不是同一个类。(ClassName函数不包括单位名称,否则会区分相同名称的类。不确定它如何处理嵌套类。) - Rob Kennedy
2
@Rob 它做了显而易见的事情:'TOuter.TInner'。 - David Heffernan
1
文档说明应该使用关键字is而不是通过ClassType进行比较。 - StanE
1
@jmiller:is关键字和比较ClassType执行两种不同的操作。大多数情况下,您希望检查一个对象是否可以赋值给特定的类,并且is可以实现这一点。但在这个特定的情况下,OP所寻找的并不是这个,而是比较ClassType - Mason Wheeler

12

在面向对象编程中,良好的实践规定不应该这样做。您所描述的是违反Liskov替换原则的直接违反,该原则指出:

 

程序中的对象应该可以替换为其子类型的实例,而不会改变程序的正确性。

我认为您应该解释一下您试图解决的问题,然后更好的方法可能就会变得明显。


2
这正是我需要的推动。 - Christopher Chase

3
另一种方法:在TParent中引入一个抽象方法,比如说CheckValidChild,并在派生类中进行覆盖。现在当你调用obj.CheckValidChild时,如果obj的实例是TParent类,则会出现EAbstractError。

1

我想我解决了我所尝试做的事情,昨晚它在我的脑海中闪现。

iParentInterface = interface(IUnknown);
TChild1 = class(TInterfacedObject,iParentInterface);     
TChild2 = class(TInterfacedObject,iParentInterface);   

Procedure DoSomething(obj:iParentInterface);

0

有一个保留字(IS)用于检查实例是类型还是类型的子类。

例如:

TFruit = class
end;
TApple = class(TFruit)
end;
TOrange = class(TFruit)
end;

procedure check;
var 
   a: TApple;
   b: TOrange;
begin
   if (a is TFruit) then showmessage('a is a fruit');
   if (b is TFruit) then showmessage('b is also a fruit');
end;

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