24

I have these classes and a procedure:

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

 Procedure DoSomething(obj:TParent);

What I would like to do is when obj is a TParent and not a descendant raise an exception.

I thought about doing something like this:

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

but it seems a bit hackish (TM)

More: What i intended is to able to pass objects that shared properties/procedures in common. After more thought, the TParent Object isn't really needed at all, what i needed was an interface object shown in my answer.

Christopher Chase
  • 2,840
  • 6
  • 36
  • 57
  • Appart from the answers already given, there certainly are valid cases to want this but not that many. Why do you need to know? – Lieven Keersmaekers Mar 10 '11 at 07:05
  • 4
    +1 for thinking that was a hack. As a rule of thumb, if you're doing type-related things with *strings*, you're probably doing something wrong. – Rob Kennedy Mar 10 '11 at 08:02

6 Answers6

40

You'll probably find the following TObject class methods useful:

  • ClassType - returns the class of an object
  • ClassParent - gives the parent class of the class
  • InheritsFrom - returns if a class inherits from another class (ie, checks the entire inheritance chain). It includes the current class.

So, you could achieve what you want (descends from TParent but not TDescendant?) with something like the following code (untested, don't have Delphi at this moment):

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

Or, if I've misunderstood and you just want to see if an object is a TParent, and not any kind of descendant at all, try:

if obj.ClassType = TParent then...

Delphi was way ahead of its time by providing access to classes via metaclasses, so rather than just checking the class name you can access an actual class object.

David
  • 13,360
  • 7
  • 66
  • 130
17

You're on the right track, but instead of comparing classnames, it would be simpler to check the ClassType property.

if obj.ClassType = TParent then raise exception.create....
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • 5
    Not only simpler, but also *more correct*. It's possible for two classes to have the same name but not be the same class. (The `ClassName` function doesn't include the unit name, which would otherwise distinguish like-named classes. Not sure what it does about nested classes.) – Rob Kennedy Mar 10 '11 at 07:55
  • 2
    @Rob It does the obvious: 'TOuter.TInner'. – David Heffernan Mar 11 '11 at 00:49
  • 1
    Documentation says that the keyword *is* should be used instead of comparing by ClassType. – StanE Jan 24 '15 at 23:26
  • 1
    @jmiller: The **is** keyword and comparing `ClassType` do two different things. Most of the time, you want to check if an object is assignment-compatible with a specific class, and **is** will do that. But in this particular case, that's not what the OP is looking for, and comparing `ClassType` is. – Mason Wheeler Jan 25 '15 at 00:53
12

Good practice in object-oriented programming states that this should not be done. What you are describing is a direct violation of the Liskov substitution principle which states that:

objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program

I think you should explain what problem you are attempting to solve and then a better approach might become apparent.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
3

Another approach: Introduce an abstract method in TParent, say CheckValidChild, and override it in the descendant classes. Now when you call obj.CheckValidChild you get an EAbstractError if the instance of obj is of class TParent.

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
1

I think ive solved what i was trying to do, It hit me on the head last night.

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

Procedure DoSomething(obj:iParentInterface);
Christopher Chase
  • 2,840
  • 6
  • 36
  • 57
0

There is a reserved word (IS) to check whether a instance is a type or a child of a type.

Example:

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;