假设您有以下内容:
//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
来解决这个问题
TMyClass
TMyClass = class(TInterfacedObject, IChild, IParent)
没关系,这无效。
答案 0 :(得分:14)
如果实现类没有声明它支持继承的接口,那么该类将不会与继承接口的变量兼容。您发布的代码示例应该可以正常工作(使用IChild接口),但是如果您尝试从TMyClass实例分配给IParent变量,那么您将遇到麻烦。
原因是因为COM和ActiveX允许实现实现后代接口(您的IChild)但拒绝该接口的祖先(IParent)。由于Delphi接口旨在与COM兼容,因此这就是这个愚蠢的工件来源。
我很确定我在大约10年或12年前写过一篇关于此事的文章,但我的Borland博客并没有幸免于向Embarcadero服务器过渡。
可能有一个编译器指令来改变这种行为,我不记得了。
答案 1 :(得分:8)
问题不在于接口声明或类实现,而在于消费者代码:
procedure CallDoSomething(Parent: TParent);
begin
if Parent is TChild then
TChild(Parent).DoSomething; // << This is wrong
end;
不能正常工作,因为 TChild 没有方法“ DoSomething ”。如果 TChild 直接实施 IChild ,那么通常可以,因为 TChild 会直接将 AND 作为 IChild 界面的一部分实施。
但请注意,如果 TChild 在 PRIVATE 范围内实施了 DoSomething ,它仍然可以通过界面访问,但正常的范围规则意味着你仍然无法使用 TChild 引用来调用它(来自class / 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;
答案 2 :(得分:0)
编辑:此答案不再相关,因为它是在原始问题被修改之前发布的。
这在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;
答案 3 :(得分:-1)
IShellView *psv = some object;
IOleView *pow;
psv->QueryInterface(IID_IOleView, (void**)&pow);
的Delphi实现不符合标准。在标题为的博客文章中
The ways people mess up IUnknown::QueryInterface Raymond Chen列举了实施中常见的失败。最值得注意的是第三点
忘记响应基接口。实现派生接口时,隐式实现基接口,所以 不要忘记回应他们。
var text = "";//Holds the text of current line being looped. var startindex = 0;//The position where selection starts. var endindex = 0;//The length of selection. for (int i = 0; i < richtextbox1.Lines.Length; i++)//Loops through each line of text in RichTextBox { text = richtextbox1.Lines[i]; //Stores current line of text. startindex = richtextbox1.GetFirstCharIndexFromLine(i); endindex = text.Length; richtextbox1.Select(startindex, endindex); MessageBox.Show(richtextbox1.SelectedText); richtextbox1.SelectedText = ""; }
有些对象忘了,而且QueryInterface因E_NOINTERFACE而失败。
除非将继承的接口显式附加到某个类或其祖先之一,否则Delphi无法找到它。它只是遍历对象的接口表及其继承的类型,并检查接口ID的匹配,它不检查基接口。