继承接口的接口委派是否需要包装类?

时间:2015-08-18 22:10:02

标签: delphi interface delphi-xe2 delphi-xe4 delphi-xe8

Delphi允许使用var cluster = require('cluster'), logger = require('./logger'), maxJobs = Math.floor(require('os').cpus().length / 2), activeJobs = 0; if (cluster.isMaster) { logger.enableConsole(); cluster.setupMaster({silent: true}); logger.log('workr', 'MaxJobs: ' + maxJobs); setInterval(function () { logger.log('workr', "Active: " + activeJobs); }, 200); for (var i = 0; i < maxJobs; i++) { cluster.fork(); } Object.keys(cluster.workers).forEach(function (id) { cluster.workers[id].process.stdout.on('data', function (chunk) { logger.log('workr', chunk); }); cluster.workers[id].process.stderr.on('data', function (chunk) { logger.log('error', 'Error in worker ' + id + ': ' + chunk); }); cluster.workers[id].on('message', function (msg) { if (msg.update !== undefined) { activeJobs += msg.update ? 1 : -1; } }); }) cluster.on('exit', function (worker, code, signal) { logger.log('workr', 'worker ' + worker.process.pid + ' died'); }); } else { setTimeout(function () { console.log('Adding 1'); process.send({ update: true }); setTimeout(function () { console.log('Removing 1'); process.send({ update: false }); }, Math.random() * 500 + 1000); }, Math.random() * 600 + 200); } 关键字进行接口委派。

例如

implements

效果很好,但不适用于继承的接口。 (错误消息&#34;缺少接口方法ILev1.foo&#34的实现;)

IIndep1 = interface
  function foo2: integer;
end;

IIndep2 = interface
  function goo2: integer;
end;

TIndep1And2 = class(TInterfacedObject, IIndep1, IIndep2)
private
  FNested : IIndep1; //e.g. passed via constructor or internally created (not shown here)
public
   Constructor Create(AIndep1: IIndep1);
  function goo2: integer;
   property AsIndep1 : IIndep1 read FNested implements IIndep1;
end;

解决方法是添加额外的祖先类

ILev1 = interface
  function foo: Integer;
end;

ILev2 = interface(ILev1)
  function goo: Integer;
end;

TLev2Fails = class(TInterfacedObject, ILev1, ILev2) //Also fails with ILev2 alone (Error: "ILev1 not mentioned in interface list")
private
  FNested : ILev1; //passed via constructor or internally created 
public
   Constructor Create(AILev1: ILev1);
   function goo: Integer;
   property AsLev1 : ILev1 read FNested implements ILev1;
end;

有没有办法避免包装类祖先?

[编辑] 只是关于接口委派的说明,使用TLev1Wrapper = class(TInterfacedObject, ILev1) private FNested : ILev1; //passed via constructor or internally created public Constructor Create(AILev1: ILev1); property AsLev1 : ILev1 read FNested implements ILev1; end; TLev2Works = class(TLev1Wrapper, ILev2) public function goo: Integer; end; 的目的是避免直接满足接口,而是将该需求传递给聚合或组合成员。提供完整的界面并手动委派给组成成员会失败使用implements来指导界面所带来的好处。实际上,在这种情况下,implements关键字和属性可能会被删除。

2 个答案:

答案 0 :(得分:2)

这似乎是编译器试图强制执行the expectations (read: requirements) of IUnknown.QueryInterface

  

对于任何一个对象,任何IUnknown接口的特定查询   对象的接口必须始终返回相同的指针值。

如果您能够在实现派生接口的同时委托基础接口的实现,那么:

obj := TLev2Fails.Create(otherLev1);  // Assuming your class could compile

lev1 := obj as ILev1;     // yields reference to otherLev1 implementor
lev2 := obj as ILev2;     // yields reference to TLev2Fails instance

unk1 := lev1 as IUnknown;   // returns IUnknown of otherLev1 implementor
unk2 := lev2 as IUnknown;   // returns IUnknown of obj TLev2fails instance

如果您的嵌套对象被正确实现为 TAggregatedObject 派生类,则不会出现这种情况,但是编译器无法知道是否是这种情况,更不用说强制执行它了,而是出现了它只需要如果你实现派生接口,那么你还必须直接实现接口本身继承的任何接口。

在这种情况下,编译器错误不是很有帮助,虽然它可以被解读为告诉你你需要做什么,而不是为什么你需要在这种情况下这样做,这对编译器错误来说并不常见。

如果您希望在这种情况下委派,那么您必须“手动委派”。

虽然这失去了implements设施的好处,但它至少保留了重复使用的好处,但不太方便。

注意:即使您的委托实现 基于 TAggregatedObject ,编译器仍然无法确定这些实现细节是否满足要求 QueryInterface ,因此您仍会收到此错误(即使使用委派接口的类引用)。

您仍必须手动委派。

说了这么多,我现在无法看到当接口没有继承关系的情况下,这有什么不同,但很可能这是有效的,我只是没有完成所有必要的工作'想实验'向我自己证明。 :)

可能是编译器在它认为可能/应该存在的情况下只是格外谨慎,而且文档根本​​没有提到implements导致的限制。

或者它可能是编译器中的一个错误,尽管我认为行为有足够明显的原因,并且行为本身如此完善且一致(在Delphi 7中重现完全相同的行为)相关的遗漏文档是更可能的解释。

答案 1 :(得分:0)

  

有没有办法避免包装类祖先?

不,继承的接口必须绑定到祖先类。由于here解释的原因,Delphi不会隐式绑定继承的接口。此外,它不能绑定到当前类,除非它手动将所有调用委托给组合项。

我们只剩下一个选项。一个祖先包装类,我们可以绑定ILev1