Dan*_*all 4 delphi oop com interface
我正在学习COM和接口,并有以下实验代码:
type
IA = interface(IInterface)
['{C9C5C992-3F67-48C5-B215-7DCE6A61F0E8}']
end;
IB = interface(IA)
['{F1799437-AD12-471B-8716-F1D93D1692FC}']
end;
IC = interface(IB)
['{01780E8C-C47D-468E-8E42-4BFF3F495D51}']
end;
TBO = class(TInterfacedObject, IB)
end;
procedure TForm1.FormCreate(Sender: TObject);
var
x: TBO;
a: IInterface;
begin
x := TBO.Create;
IInterface(x)._AddRef;
if Assigned(TBO.GetInterfaceEntry(IA)) then memo1.lines.add('GetInterfaceEntry IA: OK'); // Why not?
if Assigned(TBO.GetInterfaceEntry(IB)) then memo1.lines.add('GetInterfaceEntry IB: OK');
if Assigned(TBO.GetInterfaceEntry(IC)) then memo1.lines.add('GetInterfaceEntry IC: OK');
if x.QueryInterface(IA, a)=S_OK then memo1.lines.add('QueryInterface TA: OK'); // Why not?
if x.QueryInterface(IB, a)=S_OK then memo1.lines.add('QueryInterface TB: OK');
if x.QueryInterface(IC, a)=S_OK then memo1.lines.add('QueryInterface TC: OK');
if Supports(TBO, IA) then memo1.lines.add('Supports TA: OK'); // Why not?
if Supports(TBO, IB) then memo1.lines.add('Supports TB: OK');
if Supports(TBO, IC) then memo1.lines.add('Supports TC: OK');
if Supports(x, IA, a) then memo1.lines.add('Supports(2) TA: OK'); // Why not?
if Supports(x, IB, a) then memo1.lines.add('Supports(2) TB: OK');
if Supports(x, IC, a) then memo1.lines.add('Supports(2) TC: OK');
end;
Run Code Online (Sandbox Code Playgroud)
输出:
GetInterfaceEntry IB: OK
QueryInterface TB: OK
Supports TB: OK
Supports(2) TB: OK
Run Code Online (Sandbox Code Playgroud)
但是我需要:
GetInterfaceEntry IA: OK
GetInterfaceEntry IB: OK
QueryInterface TA: OK
QueryInterface TB: OK
Supports TA: OK
Supports TB: OK
Supports(2) TA: OK
Supports(2) TB: OK
Run Code Online (Sandbox Code Playgroud)
据我所知,由于接口继承,这IB是一个超集IA.据我所知,自TBO实现以来IB,它会自动实现IA.但是为什么Supports(),QueryInterface(),GetInterfaceEntry()返回false?
如何直接或间接查询TBO实现IA,即通过实现IA?的超集?我需要两者,像静态类函数GetInterfaceEntry和动态对象引用变体一样QueryInterface.
这是德尔福的一个着名的怪癖.即使IB从中继承IA,也TBO必须明确指定两者IA,IB以便Supports()检索两个接口.
TBO = class(TInterfacedObject, IA, IB)
Run Code Online (Sandbox Code Playgroud)
我忘了这个的技术原因.与编译器如何为其生成接口表的限制有关TBO.它不会自动包含继承的接口.
正如MarcoCantù在这里解释的那样:接口"继承"与类继承不同,实际上将它称为接口扩展可能更好.
如果我们假设一个类自动实现了一个基接口,就会出现一些严重的异常.
考虑:
IA = interface
<guid>
end;
IA1 = interface(IA)
<guid>
end;
IA2 = interface(IA)
<guid>
end;
Run Code Online (Sandbox Code Playgroud)
还有一个聚合IA1和IA2的类,其中两个都被委托:
TAggregate = class(TInterfacedObject, IA1, IA2)
private
FIA1: IA1;
FIA2: IA2;
protected
property ImplIA1: IA1 read FIA1 implements IA1;
property ImplIA2: IA2 read FIA2 implements IA2;
end;
Run Code Online (Sandbox Code Playgroud)
现在,如果你问一个TAggregate实例的IA接口,它应该返回哪个实现?
关键是接口"继承"并不是真正的继承.因此,除非明确地这样做,否则该对象不会实现"祖先"接口.
还有一些其他事情要注意:
如果添加的方法procedure MethodIA;对IA界面也还明确添加IA作为一个接口TAggregate,代码将无法编译.你会收到错误:
未申报的身份证明:'MethodIA'
您必须MethodIA直接添加到类中,或者选择将实现委托给哪个FIA1或FIA2委派实现.例如,选择FIA2:
property ImplIA: IA2 read FIA2 implements IA;
Run Code Online (Sandbox Code Playgroud)
此属性声明显示了Delphi识别接口的方式 -关系.即不是通过实现类,而是通过接口引用.
注意:即使在编译时,编译器也不会假定类实现了基接口,除非明确说明.
TIA1Implementor = class(TInterfacedObject, IA1)
protected
procedure MethodIA;
end;
var
LImplObj: TIA1Implementor;
LA1: IA1;
LA: IA;
begin
LImplObj := TIA1Implementor.Create;
LA1 := LImplObj; //Valid: TIA1Implementor implements IA1
LA := LImplObj; //Does not compile: TIA1Implementor does not implement IA
LA := LA1; //Valid: IA1 is an extension of IA
end;
Run Code Online (Sandbox Code Playgroud)
在编译时,扩展是已知的,并且为其基础分配扩展引用是完全合法的(没有任何强制转换或"支持"检查).但是直接从对象引用中分配不是.
有趣的是,该TypInfo单元显示接口类型确实知道他们的"父"接口.
PTypeData = ^TTypeData;
TTypeData = packed record
case TTypeKind of
tkInterface: (
IntfParent : PPTypeInfo; { ancestor }
IntfFlags : TIntfFlagsBase;
Guid : TGUID;
IntfUnit : ShortStringBase;
{PropData: TPropData});
Run Code Online (Sandbox Code Playgroud)
但是考虑到上面的讨论,我不确定这些知识在运行时是如何有益的.我宁愿支持编译时检查.
| 归档时间: |
|
| 查看次数: |
964 次 |
| 最近记录: |