Are Delphi interfaces inherited in subclasses - delphi

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.

Related

What is the correct code to override Assign method?

I'm confused by the Assign code in many other threads, for example, this one:
See the Assign method implementation given in an answer.
procedure TDispPitch.Assign(Source: TPersistent);
var
LSource: TDispPitch;
begin
if Source is TDispPitch then
begin
LSource := TDispPitch(Source);
iLineSize := LSource.LineSize;
iLineColor := LSource.LineColor;
bDisplayAccent := LSource.DisplayAccent;
bVisible := LSource.Visible;
Changed;
end else
inherited;
end;
But if the call to inherited is in the Else part, how are the base class properties assigned for an object that satisfies the if condition? Compare this to the VCL's own code for TStringList.
procedure TStringList.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TStringList then
begin
FCaseSensitive := TStringList(Source).FCaseSensitive;
...
end;
end;
This correctly calls Inherited first so that base class properties are assigned first.
So is the first code block right? I find such a code at many places on stack overflow. I can't understand how the base class properties are assigned in that code. Can someone explain? On the other hand, if the code is wrong, why hasn't someone pointed it out in all those threads?
Finally I got around to solving this. Actually, when I went to implement a Sort on the TCollection, it didn't work and then finally I was able to see what was wrong and how it should work. I had to look at the sources in my old Delphi XE4 version to confirm my finding.
Here are the important points to consider when writing the overridden Assign code:
The Assign in TPersistent does nothing. In fact, it catches the error if a derived class didn't implement the Assign override at the time it is needed. It shows a message that says something like "Can not assign."
So if you have derived a class straight from TPersistent, you need the code like the first example at the top. The call to Inherited doesn't really achieve anything because the real work of assigning is done by the upper If block. My collection item had the code in the second example even though it was deriving from TCollectionItem which is a TPersistent class with no Assign logic of its own. Hence, the call to Inherited which occurred first was immediately causing an exception "Can not assign" as soon as the sort tried to exchange items.
Taking this further, if your class is derived from an intermediate class based on TPersistent, you need to know if it has its own Assign logic. If yes, you must use the second example at the top so that the inherited Assign logic gets a chance to do its magic first. In my case, I had a class derived from TOwnedCollection/TCollection with its own Assign logic that copies the Items from one collection to another. So I had to use the second example at the top.
Let's try to summarize what we learned from the point of view of using TCollectionItem and TCollection/TOwnedCollection:
Assign of the class that has the ancestor TOwnedCollection/TCollection: Use the second example code block at the top so that the collection can do its assigns before yours.
Assign of the class that has the ancestor TCollectionItem/TPersistent: Use the first example code block at the top because both of these do not have their own Assigns. However, if you are deriving from another class based on these and it has its own Assign logic, you must use the second example so that its Assign executes first by the call to Inherited.

SW-Design: Adapters for Class Hierarchy in Delphi (Generics vs. Downcast)

I would like to have some suggestions for the following problem:
Let's say you want to write adapters for the VCL controls. All Adapters should have the same base class, but differ in wrapping special controls (for example getting a value from a TEdit is different from getting a value from TSpinEdit).
So the first idea is to create a class hierarchy like
TAdapter = class
end;
TEditAdapter = class (TAdapter)
end;
TSpinEditAdapter = class (TAdapter)
end;
Now I want to introduce a field to hold a reference to the vcl control. In my special adapaters I want - of course - work with the concrete subclass. But the Base class should also contain a reference (for example if I want to use the adapter to make a control visible).
Possibility 1 (Downcast in Property Accessor):
TAdapter = class
protected
FCtrl : TControl;
end;
TEditAdapter = class (TAdapter)
public
property Control : TEdit read GetControl write Setcontrol;
end;
{...}
function TEditAdapter.GetControl : TEdit;
begin
Result := FCtrl as TEdit;
end;
So if I implement a specific method I work with the property Control, if I do something in my base class I use the protected field.
Possibility 2 (Use a generic base class):
TAdapter = class
end;
TAdapter <T : TControl> = class (TAdapter)
protected
FCtrl : T;
end;
TEditAdapter = class (TAdapter <TEdit>)
end;
Which solution would you prefer? Or is there a third solution, which is even better?
Kind regards,
Christian
You can't use generics to solve this problem, because you'll be in one of two situations:
The property or method you want to "Adapt" (the Text property for example) is defined in an ancestor class. In that case you don't need generics because you can use the one adapter for the ancestor and solve the problem for all descendants.
The property or method is introduced by the class you want to adapt. In that case you can't use generics because in order to access the property or method you'd need a generic type constraint of that given type. Example. Let's say you want an adapter for the Text property of TMyClass. Let's assume TMyClass is the one introducing the Text property. In order to access it, you'd need to declare the generic type as TGeneric<T:TMyClass> and that's not actually generic.
In my opinion the best bet is to write specific adapters for each class, as in your first option. You might be able to use RTTI tricks to make your first option easier to implement, but I'm not sure it'd be worth it.
The generics version could allow to avoid some duplicated code, at least in the TAdapter class. By using the T type, it will allow a lot of shared code.
On the other hand, due to the VCL hierarchy, most used properties and methods will already be in TControl. So I'm not sure there will be so many duplicated code in non-generic implementation.
I suspect the non-generic version will produce less code and RTTI, since the current generics implementation tends not to duplicate the source, but increase the exe size.
IMHO the generic-based design will add more abstraction to the implementation, but the non-generic would perhaps be more close to the VCL hierarchy it will adapt on.
So for your particular case (mapping the VCL), since your attempt is to map non generic classes, I'd rather investigate into the non-generic solution.
For another (non VCL-based) adapter architecture, I would probably have advised a pure generic implementation, from the bottom up.

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.

Delphi Unit local variables - how to make each instance unique?

In the unit below I have a variable declared in the IMPLEMENTATION section - local to the unit. I also have a procedure, declared in the TYPE section which takes an argument and assigns that argument to the local variable in question. Each instance of this TFrame gets passed a unique variable via passMeTheVar.
What I want it to do is for each instance of the frame to keep its own version of that variable, different from the others, and use that to define how it operates. What seems to be happening, however, is that all instances are using the same value, even if I explicitly pass each instance a different variable.
ie:
Unit FlexibleUnit;
interface
uses
//the uses stuff
type
TFlexibleUnit=class(TFrame)
//declarations including
procedure makeThisInstanceX(passMeTheVar:integer);
private
//
public
//
end;
implementation
uses //the uses
var myLocalVar;
procedure makeThisInstanceX(passMeTheVar:integer);
begin
myLocalVar:=passMeTheVar;
end;
//other procedures using myLocalVar
//etc to the
end;
Now somewhere in another Form I've dropped this Frame onto the Design pane, sometimes two of these frames on one Form, and have it declared in the proper places, etc. Each is unique in that :
ThisFlexibleUnit : TFlexibleUnit;
ThatFlexibleUnit : TFlexibleUnit;
and when I do a:
ThisFlexibleUnit.makeThisInstanceX(var1); //want to behave in way "var1"
ThatFlexibleUnit.makeThisInstanceX(var2); //want to behave in way "var2"
it seems that they both share the same variable "myLocalVar".
Am I doing this wrong, in principle? If this is the correct method then it's a matter of debugging what I have (which is too huge to post) but if this is not correct in principle then is there a way to do what I am suggesting?
EDIT:
Ok, so the lesson learned here is that the class definition is just that. Many classes can go in one unit and all instances of all classes in the Type section share the implementation section of the unit.
myLocalVar is a global variable, but only visible within the unit.
A local variable would be in a procedure/function, like
procedure makeThisInstanceX(passMeTheVar: integer);
var
myLocalVar: Integer;
begin
myLocalVar := passMeTheVar;
end;
if you want an instance variable, that is each frame has its own copy, put it in the class:
type
TFlexibleUnit = class(TFrame)
procedure makeThisInstanceX(passMeTheVar:integer);
private
myLocalVar: Integer;
...
end;
You are calling the makeThisInstanceX method as a class (static) method rather than creating an instance of the class and calling it as an object method. Take a look at this reference:
http://oreilly.com/catalog/delphi/chapter/ch02.html
frame / unit / class / control
I applaud your heroic attempt to better the code. However, judging by your questions and comments I regret to inform you that your understanding is very limited.
A frame is not a unit which is not a class. A frame is a class but not every class is a frame. A frame is a control but not every control is a frame. Units have interface and implementation (and initialization and finalization) sections. Classes have private and public (and protected and published) parts.
I did not put the last paragraph in to try to teach but to allow you to gauge your understanding level. A Delphi developer ought to have no problem with the paragraph. I'm not trying to make you feel bad or to show off - just trying to help. Perhaps Stack Overflow is not the right tool for you at this time.
As somebody just learning Delphi for the first time, I might be confused by some of the seemingly redundant features. But the product has a long history and each addition made sense at the time it was added. It was also easier to learn when you only had to learn it a piece at a time.

In Delphi, how can you check if an IInterface reference implements a derived but not explicitly-supported interface?

If I have the following interfaces and a class that implements them -
IBase = Interface ['{82F1F81A-A408-448B-A194-DCED9A7E4FF7}']
End;
IDerived = Interface(IBase) ['{A0313EBE-C50D-4857-B324-8C0670C8252A}']
End;
TImplementation = Class(TInterfacedObject, IDerived)
End;
The following code prints 'Bad!' -
Procedure Test;
Var
A : IDerived;
Begin
A := TImplementation.Create As IDerived;
If Supports (A, IBase) Then
WriteLn ('Good!')
Else
WriteLn ('Bad!');
End;
This is a little annoying but understandable. Supports can't cast to IBase because IBase is not in the list of GUIDs that TImplementation supports. It can be fixed by changing the declaration to -
TImplementation = Class(TInterfacedObject, IDerived, IBase)
Yet even without doing that I already know that A implements IBase because A is an IDerived and an IDerived is an IBase. So if I leave out the check I can cast A and everything will be fine -
Procedure Test;
Var
A : IDerived;
B : IBase;
Begin
A := TImplementation.Create As IDerived;
B := IBase(A);
//Can now successfully call any of B's methods
End;
But we come across a problem when we start putting IBases into a generic container - TInterfaceList for example. It can only hold IInterfaces so we have to do some casting.
Procedure Test2;
Var
A : IDerived;
B : IBase;
List : TInterfaceList;
Begin
A := TImplementation.Create As IDerived;
B := IBase(A);
List := TInterfaceList.Create;
List.Add(IInterface(B));
Assert (Supports (List[0], IBase)); //This assertion fails
IBase(List[0]).DoWhatever; //Assuming I declared DoWhatever in IBase, this works fine, but it is not type-safe
List.Free;
End;
I would very much like to have some sort of assertion to catch any mismatched types - this sort of thing can be done with objects using the Is operator, but that doesn't work for interfaces. For various reasons, I don't want to explicitly add IBase to the list of supported interfaces. Is there any way I can write TImplementation and the assertion in such a way that it will evaluate to true iff hard-casting IBase(List[0]) is a safe thing to do?
Edit:
As it came up in the one of the answers, I'm adding the two major reasons I do not want to add IBase to the list of interfaces that TImplementation implements.
Firstly, it doesn't actually solve the problem. If, in Test2, the expression:
Supports (List[0], IBase)
returns true, this does not mean it is safe to perform a hard-cast. QueryInterface may return a different pointer to satisfy the requested interface. For example, if TImplementation explicitly implements both IBase and IDerived (and IInterface), then the assertion will pass successfully:
Assert (Supports (List[0], IBase)); //Passes, List[0] does implement IBase
But imagine that somebody mistakenly adds an item to the list as an IInterface
List.Add(Item As IInterface);
The assertion still passes - the item still implements IBase, but the reference added to the list is an IInterface only - hard-casting it to an IBase would not produce anything sensible, so the assertion isn't sufficient in checking whether the following hard-cast is safe. The only way that's guaranteed to work would be to use an as-cast or supports:
(List[0] As IBase).DoWhatever;
But this is a frustrating performance cost, as it is intended to be the responsibility of the code adding items to the list to ensure they are of the type IBase - we should be able to assume this (hence the assertion to catch if this assumption is false). The assertion isn't even necessary, except to catch later mistakes if anyone changes some of the types. The original code this problem comes from is also fairly performance critical, so a performance cost that achieves little (it still only catches mismatched types at run-time, but without the possibility to compile a faster release build) is something I'd rather avoid.
The second reason is I want to be able to compare references for equality, but this can't be done if the same implementation object is held by different references with different VMT offsets.
Edit 2: Expanded the above edit with an example.
Edit 3: Note: The question is how can I formulate the assertion so that the hard-cast is safe iff the assertion passes, not how to avoid the hard-cast. There are ways to do the hard-cast step differently, or to avoid it completely, but if there is a runtime performance cost, I can't use them. I want all the cost of checking within the assertion so that it can be compiled out later.
Having said that, if someone can avoid the problem altogether with no performance cost and no type-checking danger that would be great!
One thing you can do is stop type-casting interfaces. You don't need to do it to go from IDerived to IBase, and you don't need it to go from IBase to IUnknown, either. Any reference to an IDerived is an IBase already, so you can call IBase methods even without type-casting. If you do less type-casting, you let the compiler do more work for you and catch things that aren't sound.
Your stated goal is to be able to check that the thing you're getting out of your list really is an IBase reference. Adding IBase as an implemented interface would allow you to achieve that goal easily. In that light, your "two major reasons" for not doing that don't hold any water.
"I want to be able to compare references for equality": No problem. COM requires that if you call QueryInterface twice with the same GUID on the same object, you get the same interface pointer both times. If you have two arbitrary interface references, and you as-cast them both to IBase, then the results will have the same pointer value if and only if they are backed by the same object.
Since you seem to want your list to only contain IBase values, and you don't have Delphi 2009 where a generic TInterfaceList<IBase> would be helpful, you can discipline yourself to always explicitly add IBase values to the list, never values of any descendant type. Whenever you add an item to the list, use code like this:
List.Add(Item as IBase);
That way, any duplicates in the list are easy to detect, and your "hard casts" are assured to work.
"It doesn't actually solve the problem": But it does, given the rule above.
Assert(Supports(List[i], IBase));
When the object explicitly implements all its interfaces, you can check for things like that. And if you've added items to the list like I described above, it's safe to disable the assertion. Enabling the assertion lets you detect when someone has changed code elsewhere in your program to add an item to the list incorrectly. Running your unit tests frequently will let you detect the problem very soon after it's introduced, too.
With the above points in mind, you can check that anything that was added to the list was added correctly with this code:
var
AssertionItem: IBase;
Assert(Supports(List[i], IBase, AssertionItem)
and (AssertionItem = List[i]));
// I don't recall whether the compiler accepts comparing an IBase
// value (AssertionItem) to an IUnknown value (List[i]). If the
// compiler complains, then simply change the declaration to
// IUnknown instead; the Supports function won't notice.
If the assertion fails, then either you added something to the list that doesn't support IBase at all, or the specific interface reference you added for some object cannot serve as the IBase reference. If the assertion passes, then you know that List[i] will give you a valid IBase value.
Note that the value added to the list doesn't need to be an IBase value explicitly. Given your type declarations above, this is safe:
var
A: IDerived;
begin
A := TImplementation.Create;
List.Add(A);
end;
That's safe because the interfaces implemented by TImplementation form an inheritance tree that degenerates to a simple list. There are no branches where two interfaces don't inherit from each other but have a common ancestor. If there were two decendants of IBase, and TImplementation implemented them both, the above code wouldn't be valid because the IBase reference held in A wouldn't necessarily be the "canonical" IBase reference for that object. The assertion would detect that problem, and you'd need to add it with List.Add(A as IBase) instead.
When you disable assertions, the cost of getting the types right is paid only while adding to the list, not while reading from the list. I named the variable AssertionItem to discourage you from using that variable elsewhere in the procedure; it's there only to support the assertion, and it won't have a valid value once assertions are disabled.
You are right in your examination and as far as I can tell there is really no direct solution to the problem you've encountered. The reasons lies in the nature of inheritance among interfaces, which has only a vague resemblance of inheritance among classes.
An inherited interfaces is a brand new interface, that has some methods in common with the interface it inherits from, but no direct connection. So by choosing not to implement the base class interface, you are making a specific assumption that the compiled program will follow: TImplementation does not implement IBase.
I think "interface inheritance" is somewhat of a misnomer, interface extension makes more sense! A common practice is to have a base class implementing the base interface, and than derived classes implementing the extended interfaces, but in case you want a separate class that implements both simply list those interfaces. It there a specific reason you want to avoid using:
TImplementation = Class(TInterfacedObject, IDerived, IBase)
or just you don't like it?
Further Comment
You should never, even hard type cast an interface. When you do "as" on an interface it will adjust the object vtable pointers in the right way... if you do a hard cast (and have methods to call) you code can easily crash. My impression is that you are treating interfaces like objects (using inheritance and casts in the same way) while their internal working is really different!
In Test2;
You shound't retype IDerived as IBase by IBase(A) but with:
Supports(A, IBase, B);
And adding to list can be just:
List.Add(B);

Resources