How can I determine whether a Delphi object is of a specific class and not any descendant class? - delphi

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.

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.

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....

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.

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.

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);

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;

Related

Can a Delphi generic class descend from its class argument?

I've been trying to define a generic, inheritable TSingleton class. Here's what I had in progress:
TSingleton<RealClass, InheritsFrom : class> = class(InheritsFrom)
strict private
class var FInstance : RealClass;
protected
procedure InstanceInitialization;virtual;
public
destructor Destroy; override;
class procedure Create; reintroduce;
class function Instance : RealClass;
class procedure InstanceFree;
end;
The goal was to be able to "insert" the singleton pattern in an inheritance tree. so instead of declaring something like this :
TMySingletonComponent = class(TComponent)
end;
And need to implement the singleton pattern there, I would declare something like this :
TMyGenericSingletonComponent = class(TSingleton<TMyGenericSingletonComponent,TComponent>)
end;
Sadly, this won't work. I'm getting the following error(In D2010):
TSingleton<RealClass, InheritsFrom : class> = class(InheritsFrom) ///E2021 Class type required
Now I was wondering, would this work in Delphi XE? Is there some "clean hack" I could use to make this work in D2010? Is there some fundamental reasons why this can't work?
By design, you can't create a generic class which derives from one of its type arguments.
No, that won't work. You're trying to define a class in terms of itself. Whatever you put inside the parameters has to be fully defined already.
What do you want to obtain?
IMHO, singletons are evil. They were introduced because of bad OOP design of C++ (for access to input/output streams in console applications, as far as I remember). And they tend to be like hell to maintain.
You can always live without them. It's definitively not a "Delphi classical" way of programing, because Delphi doesn't suffer the C++ problems I mentioned.
Some Java project (ab)uses of singleton. Google for it, and you'll find out what I mean.
Use a property of a common class with a getter, initializing an instance if the corresponding field is still nil, or directly returning the field pointer to the instance if it was already created. You'll have the singleton feature, with good performance, nice code, good OOP practice (no "global" class), and the ability to run the class without any singleton feature, if you don't need this feature later (for testing purpose, for instance).

Delphi class references... aka metaclasses... when to use them

I've read the official documentation and I understand what class references are but I fail to see when and why they are best solution compared to alternatives.
The example cited in the documentation is TCollection which can be instantiated with any descendant of TCollectionItem. The justification for using a class reference is that it allows you to invoke class methods whose type is unknown at compile time (I assume this is the compile time of TCollection). I'm just not seeing how using TCollectionItemClass as an argument is superior to using TCollectionItem. TCollection would still be able to hold any descendant of TCollectionItem and still be able to invoke any method declared in TCollectionItem. Wouldn't it?
Compare this with a generic collection. TObjectList appears to offer much the same functionality as TCollection with the added benefit of type safety. You are freed from the requirement to inherit from TCollectionItem in order to store your object type and you can make a collection as type specific as you want. And if you need to access item's members from within the collection you can use generic constraints. Other than the fact that class references are available to programmers prior to Delphi 2009 is there any other compelling reason to use them over generic containers?
The other example given in the documentation is passing a class reference to a function that acts as an object factory. In this case a factory for creating objects of type TControl. Its not really apparent but I'm assuming the TControl factory is invoking the constructor of the descendant type passed to it rather than the constructor of TControl. If this is the case then I'm starting to see at least some reason for using class references.
So I guess what I'm really trying to understand is when and where class references are most appropriate and what do they buy a developer?
MetaClasses and "class procedures"
MetaClasses are all about "class procedures". Starting with a basic class:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Because DoSomething is a class procedure we can call it without an instance of TAlgorithm (it behaves like any other global procedure). We can do this:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
Once we've got this setup we might create some alternative algorithms, all sharing some bits and pieces of the base algorithm. Like this:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
We've now got one base class and two descendent classes. The following code is perfectly valid because we declared the methods as "class" methods:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Let's introduce the class of type:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass is a data type that can be used like any other data type, it can be stored in a variable, passed as a parameter to a function. In other words we can do this:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
In this example the procedure CrunchSomeData can use any variation of the algorithm as long as it's an descendant of TAlgorithm.
Here's an example of how this behavior may be used in a real-world application: Imagine a payroll-type application, where some numbers need to be calculated according to an algorithm that's defined by Law. It's conceivable this algorithm will change in time, because the Law is some times changed. Our application needs to calculate salaries for both the current year (using the up-to-date calculator) and for other years, using older versions of the algorithm. Here's how things might look like:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Virtual Constructors
A Delphi Constructor works just like a "class function"; If we have a metaclass, and the metaclass knows about an virtual constructor, we're able to create instances of descendant types. This is used by TCollection's IDE Editor to create new items when you hit the "Add" button. All TCollection needs to get this working is a MetaClass for a TCollectionItem.
Yes a Collection would still be able to hold any descendant of TCollectionItem and to invoke methods on it. BUT, it wouldn't be able to instantiate a new instance of any descendant of a TCollectionItem. Calling TCollectionItem.Create constructs an instance of TCollectionItem, whereas
private
FItemClass: TCollectionItemClass;
...
function AddItem: TCollectionItem;
begin
Result := FItemClass.Create;
end;
would construct an instance of whatever class of TCollectionItem descendant is held in FItemClass.
I haven't done much with generic containers, but I think that given a choice, I would opt for the generic container. But in either case I'd still have to use a metaclass if I wanted to have the list instantiate and do whatever else needs to be done when an item is added in the container and I do not know the exact class beforehand.
For example an observable TObjectList descendant (or generic container) could have something like:
function AddItem(aClass: TItemClass): TItem;
begin
Result := Add(aClass.Create);
FObservers.Notify(Result, cnNew);
...
end;
I guess in short the advantage/benefit of metaclasses is any method/class having only knowledge of
type
TMyThing = class(TObject)
end;
TMyThingClass = class of TMyThing;
is able to construct instances of any descendant of TMyThing whereever they may have been declared.
Generics are very useful, and I agree that TObjectList<T> is (usually) more useful than TCollection. But class references are more useful for different scenarios. They're really part of a different paradigm. For example, class references can be useful when you have a virtual method that needs to be overridden. Virtual method overrides have to have the same signature as the original, so the Generics paradigm doesn't apply here.
One place where class references are used heavily is in form streaming. View a DFM as text sometime, and you'll see that every object is referred to by name and class. (And the name is optional, actually.) When the form reader reads the first line of an object definition, it gets the name of the class. It looks it up in a lookup table and retrieves a class reference, and uses that class reference to call that class's override of TComponent.Create(AOwner: TComponent) so it can instantiate the right kind of object, and then it starts applying the properties described in the DFM. This is the sort of thing that class references buy you, and it can't be done with generics.
I also would use a metaclass whenever I need to be able to make a factory that can construct not only one hard-coded class, but any class that inherits from my base class.
Metaclasses are not the term I am familiar with in Delphi circles however. I believe we call them class references, which has a less "magic" sounding name, so it's great that you put both common monikers in your question.
A concrete example of a place I have seen this used well is in the JVCL JvDocking components where a "docking style" component provides metaclass information to the base docking component set, so that when a user drags their mouse and docks a client form to the docking host form, the "tab host" and "conjoin host" forms that show grabber bars (similar in appearance to the title bar of a regular undocked window) can be of a user-defined plug-in class, that provides a customized appearance and customized runtime functionality, on a plug-in basis.
In some of my applications I have a mechanism that connects classes to forms capable of editing instances of one or more of that classes. I have a central list where those pairs are stored: a class refernce and a form class reference. Thus when I have an instance of a class I can lookup the corresponding form class, create a form from it and let it edit the instance.
Of course this could also be implemented by having a class method returning the appropriate form class, but that would require the form class to be known by the class. My approach makes a more modular system. The form must be aware of the class, but not the other way round. This can be a key point when you cannot change the classes.

Static classes in Delphi (Win32)

Is it in Delphi (Win32) possible to declare a whole class (not only a function of the class) as static?
Looks like user search for "class functions":
type
TSomeClass = class
class procedure MyFunction(...);
This is like static method, so, call it:
TSomeClass.MyFunction(...);
I assume you mean static classes like in .net (and not "static" as in traditional Delphi/Native) - and the answer to that is no.
I would use an abstract class (not to be confused with an abstract method in a class) to prevent it from being instantiated instead of demoting the constructor to protected:
TMyClass = class abstract
public
class procedure DoStuff; static;
end;
That will enforce the singleton pattern and prevent any instantiations period.
I am not quite sure what you mean by a "static class". You can declare a class, that has only class methods, so these methods can be called without instantiating the class.
TSomeClass.SomeMethod;
Is that what you want?
Not natively.
Depending on what you need it for, if for the purposes of your code, in some use cases you could replace it with a Singleton Pattern object.
For walkthrough on implementing this I'd recommend this guide which, covers almost any version of delphi, but if you're using Delphi 2010 you could also use the new class Constructors/Destructors for improved results.
You could create a class that contains nothing but static methods. If you have to maintain some sort of state, then the state variables should be passed as var parameters. There is no way to "properly" access static variables other than having a set of global variables in the implementation section of the class OUTSIDE the scope of the class, for example:
UNIT TestUnit;
INTERFACE
Type
TStaticClass = Class
public
procedure Foo(var Bar:String); static;
end;
IMPLEMENTATION
var
LastBar : String; // the last bar
LastRes : string; // the last result
Procedure TStaticClass.Foo(var Bar:String);
begin
if Bar <> LastBar then
LastRes := SomeCPUExpensiveProcess(Bar);
LastBar := Bar;
Bar := LastRes;
end;
INITIALIZATION
LastBar := '';
LastRes := SomeCPUExpensiveProcess('');
END.
You can also create a new unit called uDG_Utils for example, define a class, define a global variable for that class and in the initialization and finalization section you manage the class constructor and destructor.
Now all you need to do is call it like mySuperDuperClass.SuperMethod...
EDIT
I have edited this post to remove it. The answer is admittedly bad and deserves the down-votes. I don't want it to remain here to confuse or mislead anyone further.

Class Helper for generic class?

I'm using Delphi 2009. Is it possible to write a class helper for a generic class, i.e. for TQueue . The obvious
TQueueHelper <T> = class helper of TQueue <T>
...
end;
does not work, nor does
TQueueHelper = class helper of TQueue
...
end;
As documented in the Delphi help, class helpers are not designed for general purpose use and they are incorrectly perceived as having a number of limitations or even bugs as a result.
nevertheless there is a perception - incorrect and dangerous in my view - that these are a legitimate tool in the general purpose "toolkit". I have blogged about why this is wrong and subsequently about how you can go some way to mitigate the dangers by following a socially responsible coding pattern (although even this isn't bullet proof).
You can achieve much the effect of a class helper without any of these bugs or limitations or (most importantly) risks by using a hard cast to a "pseudo" class derived from the class you are trying to extend. i.e instead of:
TFooHelper = class helper for TFoo
procedure MyHelperMethod;
end;
use
TFooHelper = class(TFoo)
procedure MyHelperMethod;
end;
Just like with a "formal" helper, you never instantiate this TFooHelper class, you use it solely to mutate the TFoo class, except in this case you have to be explicit. In your code when you need to use some instance of a TFoo using your "helper" methods you then have to hard cast:
TFooHelper(someFoo).MyHelperMethod;
Downsides:
you have to stick to the same rules that apply to helpers - no member data etc (not really a downside at all, except that the compiler won't "remind you").
you have to explicitly cast to use your helper
If using a helper to expose protected members you have to declare the helper in the same unit that you use it (unless you expose a public method which exposes the required protected members)
Advantages:
Absolutely NO risk that your helper will break if you start using some other code that "helps" the same base class
The explicit typecasting makes it clear in your "consumer" code that you are working with the class in a way that is not directly supported by the class itself, rather than fudging and hiding that fact behind some syntactic sugar.
It's not as "clean" as a class helper, but in this case the "cleaner" approach is actually just sweeping the mess under the rug and if someone disturbs the rug you end up with a bigger mess than you started with.
I currently still use Delphi 2009 so I thought I'd add a few other ways to extend a generic class. These should work equally well in newer versions of Delphi. Let's see what it would look like to add a ToArray method to a List class.
Interceptor Classes
Interceptor classes are classes that are given the same name as the class they inherit from:
TList<T> = class(Generics.Collections.TList<T>)
public
type
TDynArray = array of T;
function ToArray: TDynArray;
end;
function TList<T>.ToArray: TDynArray;
var
I: Integer;
begin
SetLength(Result, self.Count);
for I := 0 to Self.Count - 1 do
begin
Result[I] := Self[I];
end;
end;
Notice you need to use the fully qualified name, Generics.Collections.TList<T> as the ancestor. Otherwise you'll get E2086 Type '%s' is not completely defined.
The advantage of this technique is that your extensions are mostly transparent. You can use instances of the new TList anywhere the original was used.
There are two disadvantages to this technique:
It can cause confusion for other developers if they aren't aware that you've redefined a familiar class.
It can't be used on a sealed class.
The confusion can be mitigated by careful unit naming and avoiding use of the "original" class in the same place as your interceptor class. Sealed classes aren't much of a problem in the rtl/vcl classes supplied by Embarcadero. I only found two sealed classed in the entire source tree: TGCHandleList(only used in the now defunct Delphi.NET) and TCharacter. You may run into issues with third party libraries though.
The Decorator Pattern
The decorator pattern lets you extend a class dynamically by wrapping it with another class that inherits its public interface:
TArrayDecorator<T> = class abstract(TList<T>)
public
type
TDynArray = array of T;
function ToArray: TDynArray; virtual; abstract;
end;
TArrayList<T> = class(TArrayDecorator<T>)
private
FList: TList<T>;
public
constructor Create(List: TList<T>);
function ToArray: TListDecorator<T>.TDynArray; override;
end;
function TMyList<T>.ToArray: TListDecorator<T>.TDynArray;
var
I: Integer;
begin
SetLength(Result, self.Count);
for I := 0 to Self.Count - 1 do
begin
Result[I] := FList[I];
end;
end;
Once again there are advantages and disadvantages.
Advantages
You can defer introducing the new functionally until its actually needed. Need to dump a list to an array? Construct a new TArrayList passing any TList or a descendant as a parameter in the constructor. When you're done just discard the TArrayList.
You can create additional decorators that add more functionality and combine decorators in different ways. You can even use it to simulate multiple inheritance, though interfaces are still easier.
Disadvantages
It's a little more complex to understand.
Applying multiple decorators to an object can result in verbose constructor chains.
As with interceptors you can't extend a sealed class.
Side Note
So it seems that if you want to make a class nearly impossible to extend make it a sealed generic class. Then class helpers can't touch it and it can't be inherited from. About the only option left is wrapping it.
As near as I can tell, there's no way to put a class helper on a generic class and have it compile. You ought to report that to QC as a bug.

Are Delphi interfaces inherited in subclasses

If I implement an interface on a base class will it be inherited by its sub classes, I know the functions/procedures will be, but I am more interested in whether I will be able to cast the subclass to the interface and then back to its original class.
What I am hoping I can do is pass objects of different base classes to a function, and then in the function determin there type and use them as appropriate.
Is this possible and is it the correct approach?
Update
to help clear up any confusion (or to create some more) here is what I would like to do (striped down to its core).
Interface
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
end;
Base Class
type TMyObject = class(TInterfacedObject,IMyInterFace)
Sub Class
type TSubMyObject = class(TMyObject)
Another Class
type TMyOtherObject = class(TInterfacedObject,IMyInterFace)
Then the usage
procedure foo();
var obj:TSubMyObject;
begin
obj := TSubMyObject.Create();
SendInterfaceObject(obj);
end;
procedure bar();
var obj:TMyOtherObject;
begin
obj := TMyOtherObject.Create();
SendInterfaceObject(obj);
end;
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end
else if (iobj is TMyOtherObject) then
begin
//code here
end;
end;
Update 2
I have updated the code abit so show what I am after better.
the //code here sections have little to do with the object that are passed to it, for example if this class is TAccounts and it was passed a TEmployee object it may pay there weekly pay but if it was a TInvoice then it would check to see if it needed paying and only pay it when the date was 2 days before the dead line.
the TEmployee/TInvoice may even come from out side classes asking for payments to be made.
this is just an example.
Yes, the interface is inherited by the subclass.
It's perfectly acceptable to cast from subclass to the interface.
However, and apologies if I'm reading your question wrong, but if "and then back to its original class" means . . .
You have Interface I, class A and class B.
A implements I, and B inherits A, you possibly can, but REALLY SHOULD NOT cast from A to B.
EDIT:
You want to go from B to I and back to B . . . but you already have a reference to B, if B is what you pass to your function, so you don't need to cast from I to B (unless were talking about a different object, then No, don't do it)
Going from I to B is the same as going from A to B, you're trying to cast up the inheritance chain, which really is something you shouldn't do. Needing to do this is a code smell, it tells you that you should try and solve this problem in a different way (possibly by redesigning you classes (e.g. adding more properties / methods to I), or just deciding that the function will only work with the sub class - working with the subclass 'B' will give you access to all the methods of A & I).
Can you edit your question and add some sample code of what you're trying to do?
EDIT 2
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end;
end;
The 'If' statement in there is a bad idea, and breaks OO principals. If you need to do this then either
The interface definition is
insufficient, you might want to add a
Type property to the interface
allowing you to (if iObj.SomeProperty
= 1) then . . .)
The interface is simply not the
correct soluton to this problem, and
you should pass the reference as
TSubMyObject.
EDIT 3:
#mghie: I agree with you, what I didn't explain very well was that SomeProperty has some data that allows the function to branch there, removing the dependancy of type checking. SomeProperty shouldn't 'simply' replace the type check (e.g. by putting the class name in a property, then checking the class name) That is indeed exactly the same problem.
There is some essential difference between Subclasses that inherit the interface. This difference should be expressed by either
Exposing some item of data that can
then be used in the brach
e.g.
if(item.Color = Red) then
item.ContrastColor := Blue;
else
item.ContrastColor := Red;
Or through polymorphism e.g.
IEmployee defines a CalculatePay method, TManager and TWorker implement IEmployee, each with different logic in the CalculatePay methods.
If the intent was to do something like the first case, polymorphism could be overkill (polymorphism doesn't fix every problem).
EDIT 4
You say "the //code here sections have little to do with the object that are passed to it . . ." I'm sorry but that statement is incorrect, if you need to Pay an Employee, you need to know their 1) EmployeeCode 2) Their Salary Details 3) Their bank details etc, if you're charging an invoice you need 1) InvoiceNumber 2) Invoice Amount 3) CustomerCode to charge to etc . . . this is an ideal place for Polymorphism.
Lets say the function taking the interface checks to see if "Accounts" needs to do something with the object (e.g. Pay the employee, charge an Invoice etc). So we might call the function AccountsCheck. Inside Accounts check you will have a peice of logic specific to each sub class (to pay an employee, to charge the invoice . . .) This is an ideal candidate for Polymorphism.
On you interface (or on another interface, or as a virtual method on the sub class) Define an "AccountsCheck" method. Then each derived class gets its own implementation of Accounts check.
The code moves out of your humungous single AccountsCheck function, and into smaller functions on each sub class. This makes the code
More obvious in intent (each class
contains some logic for
AccountsCheck)
You're less likely to break SubClass
B's logic when fixing something in
AccountsCheck for C
It's easier to figure out exactly
what SubClass B's AccountsCheck logic
is, you've only to check 20 lines of
code in small AccountsCheck, not 200
in the General AccountsCheck)
There are more, "good reasons" for this, aif nyone wants to edit/post comments please do so.
If you find you need to share some logic between implementations of AccountsCheck, create some utility functions, don't reimplement the same wheel in each one.
Polymorphism is the solution to your problem.
My suggestion here would be to not cast against the class but instead cast against another interface. Change your TMyOtherObject to:
type
IOtherObjectIntf = interface
['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
end;
type
TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)
and then change your other routine to read:
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if Supports(iObj,IOtherObjectIntf) then
begin
//code here for TMyOtherObject
end
else
begin
//code here for other standard implementations
end;
end;
This way your "custom" code for the TMyOtherObject would also be applied to any of ITS descendants without any further custom code. The IOtherObjectIntf interface is used as nothing other than a "yep, I'm one of those" indicators which allows your code to branch properly. Sure, its laying waste to another Guid...but there are so many of them, who would notice? :)
The interface is inherited by the subclasses and you can cast the objects to the interface, but it is not safe(or recommended) to cast the interface to class. If you need to do this you are probably using interfaces the wrong way.
There seems to be some doubt on how your question is to be understood, and indeed in your comment to this answer you say that you want to "go from B to I to B".
This is indeed not recommended and only supported by using information about how interfaces are implemented on a class.
If I understand you correctly then what you want to do is to pass an interface to some method, and in that method do different things depending on what concrete class the interface was implemented by. It is however best to continue using interfaces once you start with them. You could let the interface have a method to return the implementing class, but you should not make any assumptions about what class the interface is implemented in - it costs you some of the benefits of programming against interfaces.
What you can do instead is create different interfaces, and implement some of them only in (some of) your descendant classes. Then you can use QueryInterface() or Supports() on the passed interface pointer. For your base class this will return nil, but for all descendant classes that implement the interface it will return a pointer that lets you call the methods only they have.
Edit: For example in the OmniThreadLibrary you will find:
IOmniWorker = interface
['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
function GetImplementor: TObject;
...
end;
which you could add to your interface. But again, IMHO the use of distinct interfaces is much better.
You can't cast an interface to an object directly (it is not what interfaces are intended for) but sometimes it so practical to be able to do it, that you can't resist...
If you really want to do like that, you can use the example "IOmniWorker" given by mghie directly in IMyInterFace:
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
function GetImplementor: TObject;
end;
The function implementations look like that:
function TMyObject.GetImplementor: TObject;
begin
Result := Self;
end;
function TMyOtherObject.GetImplementor: TObject;
begin
Result := Self;
end;
SendInterfaceObject looks then like that:
procedure SendInterfaceObject(const iobj:IMyInterFace);
begin
if (iobj.GetImplementor is TSubMyObject) then
begin
//code here
end
else if (iobj.GetImplementor is TMyOtherObject) then
begin
//code here
end;
end;
By the way I have added a (very) small optimization: by passing iobj as "const" to the function you can avoid unnecessary reference counting.

Resources