[英]Delphi interface inheritance: Why can't I access ancestor interface's members?
假设您具有以下条件:
//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;
我认为这将允许您调用DoSomething
但事实并非如此:
procedure CallDoSomething(Parent: TParent);
begin
if Parent is TChild then
TChild(Parent).DoSomething;
end;
很明显,编译器正在强制接口继承,因为除非实现IParent
的成员,否则两个类都不会编译。 尽管如此,在实例化和使用IParent
时,编译器仍无法解析IParent
成员。
我可以通过在 IParent
的类声明中明确包含IParent
来解决此TMyClass
:
TMyClass = class(TInterfacedObject, IChild, IParent)
没关系,这不会解决任何问题。
如果实现类未声明其支持继承的接口,则该类的分配将与继承的接口的变量不兼容。 您发布的代码示例应该可以正常工作(使用IChild接口),但是如果您尝试将TMyClass的实例分配给IParent变量,则会遇到麻烦。
原因是因为COM和ActiveX允许实现实现后代接口(您的IChild),但拒绝该接口的祖先(IParent)。 由于Delphi接口旨在与COM兼容,因此这就是愚蠢的工件的来源。
我敢肯定,大约10或12年前,我写了一篇有关此的文章,但是我的Borland博客在过渡到Embarcadero服务器后仍然无法幸免。
我不记得有可能要更改此行为的编译器指令。
问题不在接口声明或类实现中,而在使用者代码中:
procedure CallDoSomething(Parent: TParent);
begin
if Parent is TChild then
TChild(Parent).DoSomething; // << This is wrong
end;
由于TChild没有方法“ DoSomething ”,因此无法正常工作。 如果年掳 直接实现ICHILD,那么这通常是可能的,因为年掳将直接作为ICHILD接口的一部分实现的方法。
但是请注意,如果TChild在PRIVATE范围内实现DoSomething ,它将可以通过接口访问,但是常规的作用域规则将意味着您仍然无法使用TChild引用(从类/ uni外部)调用它。
就您而言,您只需要获取适当的接口,然后通过该接口调用所需的方法:
if Parent is TChild then
(Parent as IChild).DoSomething;
但是,您正在使用类类型测试来依赖于实现细节(知道TChild实现IChild的知识)来确定(推断)接口的存在。 我建议您改为直接使用接口测试,以将依赖关系与那些实现细节隔离开:
var
parentAsChild: IChild;
begin
if Parent.GetInterface(IChild, parentAsChild) then
parentAsChild.DoSomething;
end;
编辑:此答案不再相关,因为它在修改原始问题之前发布。
可以在Delphi 2010中进行编译:
type
IParent = interface(IInterface)
function DoSomething: String;
end;
IChild = interface(IParent)
function DoSomethingElse: string;
end;
TMyClass = class(TInterfacedObject, IChild)
private
public
function DoSomething: String;
function DoSomethingElse: String;
end;
// ...
procedure Test;
var
MyObject : IChild;
begin
MyObject := TMyClass.Create;
MyObject.DoSomething;
end;
QueryInterface
的Delphi实现不符合标准。 在标题为“人们弄乱的方式IUnknown :: QueryInterface ”的博客文章中, Raymond Chen列举了实现中的常见失败。 最值得注意的是第三点
忘记响应基本接口。 当实现派生接口时,您将隐式实现基本接口,因此也请不要忘记响应它们。
IShellView *psv = some object; IOleView *pow; psv->QueryInterface(IID_IOleView, (void**)&pow);
一些对象忘记了,QueryInterface失败并显示E_NOINTERFACE。
除非将继承的接口显式附加到类或其祖先之一,否则Delphi无法找到它。 它仅遍历对象的接口表及其继承的类型,并检查接口ID是否匹配,而不检查基本接口。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.