[英]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.