Assume you have the following:
//Note the original example I posted didn't reproduce the problem so
//I created an clean example
type
IParent = interface(IInterface)
['{85A340FA-D5E5-4F37-ABDD-A75A7B3B494C}']
procedure DoSomething;
end;
IChild = interface(IParent)
['{15927C56-8CDA-4122-8ECB-920948027015}']
procedure DoSomethingElse;
end;
TGrandParent = class(TInterfacedObject)
end;
TParent = class(TGrandParent)
end;
TChild = class(TParent, IChild)
private
FChildDelegate: IChild;
public
property ChildDelegate:IChild read FChildDelegate implements IChild;
end;
TChildDelegate = class(TInterfacedObject, IChild)
public
procedure DoSomething;
procedure DoSomethingElse;
end;
I would think that this would allow you to call DoSomething
but this doesn't seem to be the case:
procedure CallDoSomething(Parent: TParent);
begin
if Parent is TChild then
TChild(Parent).DoSomething;
end;
Its clear that the compiler is enforcing the interface inheritance because neither class will compile unless the members of IParent
are implemented. Despite this the compiler is unable to resolve members of the IParent
when the class is instantiated and used.
I can work around this by explicitly including IParent
in the class declaration of
TMyClass
:
TMyClass = class(TInterfacedObject, IChild, IParent)
Nevermind, this doesn't work around anything.
If an implementing class does not declare that it supports an inherited interface, then the class will not be assignment compatible with variables of the inherited interface. The code sample you posted should work fine (using the IChild interface), but if you try to assign from an instance of TMyClass to a variable of IParent, then you'll run into trouble.
The reason is because COM and ActiveX allow an implementation to implement a descendent interface (your IChild) but deny the ancestor of that interface (IParent). Since Delphi interfaces are intended to be COM compatible, that's where this goofy artifact comes from.
I'm pretty sure I wrote an article about this about 10 or 12 years ago, but my Borland blog did not survive the transition to the Embarcadero server.
There may be a compiler directive to change this behavior, I don't recall.
The problem is not in the interface declarations or class implementations, but in your consumer code:
procedure CallDoSomething(Parent: TParent);
begin
if Parent is TChild then
TChild(Parent).DoSomething; // << This is wrong
end;
Is not going to work because TChild does not have a method "DoSomething". If TChild implemented IChild directly, then this would normally be possible because TChild would implement the method directly AND as part of the IChild interface.
Note however, that if TChild implemented DoSomething in PRIVATE scope, it would remain accessible thru the interface but normal scoping rules would mean that you still couldn't invoke it (from outside the class/uni) using a TChild reference either.
In your case, you simply need to obtain the appropriate interface and then invoke the method you require thru the interface:
if Parent is TChild then
(Parent as IChild).DoSomething;
However, you are using a class type test to determine (infer) the presence of an interface, relying on an implementation detail (knowledge that TChild implements IChild). I suggest you should instead be using interface testing directly, to isolate this dependency from those implementation details:
var
parentAsChild: IChild;
begin
if Parent.GetInterface(IChild, parentAsChild) then
parentAsChild.DoSomething;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With