【问题标题】:Is interface inheritance nothing more than syntactic sugar?接口继承只不过是语法糖吗?
【发布时间】:2014-09-18 20:23:12
【问题描述】:

下面的代码不起作用。

program Project7;    
{$APPTYPE CONSOLE}
{$R *.res}

uses System.SysUtils;

type
  I1 = interface
    ['{B4BF44AD-23A9-4F42-BA2B-6E137E22344E}']
    procedure Test1;
  end;

  I2 = interface(I1)
    ['{AAAAAAAA-23A9-4F42-BA2B-BBBBBBBBBBBB}']
    procedure Test2;
  end;

  T12 = class(TInterfacedObject, I2)
  public
    procedure Test1;
    procedure Test2;
  public
    function MeAsI1: I1;
    function MeASI2: I2;
  end;

function T12.MeAsI1: I1;
begin
  Result:= (self as I1);
end;

function T12.MeASI2: I2;
begin
  Result:= (self as I2);
end;

procedure T12.Test1;
begin
 Writeln('T12: test1 from interface i1');
end;

procedure T12.Test2;
begin
   Writeln('T12: test2 from interface i2');
end;

实施:

var
  MyClass: T12;
  AI1: I1;
  AI2: I2;
begin
  MyClass:= T12.Create;
  AI2:= MyClass.MeAsI2;
  AI2.Test2;
  Readln;
  AI1:= MyClass.MeAsI1;    //<< Exception interface not supported
  AI1.Test1;
  Readln;
end.

它给出了一个例外:interface not supported
看起来 interface inheritance 不像 class inheritance 那样工作。
如果我将接口 I1 添加到类 T12 中,它确实可以工作,但是当一个类实现许多接口时,这会变得有点傻。
有没有办法只声明 I2 并且仍然能够从类内部返回对 I1 的引用?
我使用的是Delphi XE6,但我确定Delphi 3中的错误是一样的。

编辑
一个用例是:

 IReadOnly = interface
   function GetSomething: integer;
   ...

 IRWIntf = interface(IReadOnly)
   procedure SetSomething(value: integer);
   ....

【问题讨论】:

  • 您的 I2 缺少 GUID
  • 由于 AI2 被声明为 I2 并且 I2 错过了 MeAsI1 的声明,这甚至不应该编译。
  • 没有。该类没有实现I1。致电支持人员自行检查?
  • 复制粘贴,I2 没有 GUID,正打算换一个数字,但分心了。
  • 好的。认为这样。在适当的时候你会接受你的类没有实现I1

标签: delphi inheritance interface


【解决方案1】:

这是设计使然。您只能从类声明中明确提及的类中检索那些接口。

关于您的示例:由于 I2 是从 I1 继承的,因此您可以直接从 AI2 调用 I1 的方法,也可以在需要 I1 接口的任何地方传递 AI2。

【讨论】:

  • 是的,我知道,但我喜欢有一个有限的接口和一个扩展的接口,可以公开更多的private 功能。对外部用户我返回有限接口,对内部类我公开扩展接口。
  • 谢谢,设计部分很有意义。
【解决方案2】:

接口继承遵循接口的逻辑。使用接口,您可以在没有任何实现的情况下定义声明。所以继承的接口有相同的声明,仅此而已,因此不会有实现继承。

但您可以执行以下操作

procedure UseI1( AI1 : I1 );
begin
  AI1.Test1;
end;

procedure UseI2( AI2 : I2 );
begin
  AI2.Test1;
  AI2.Test2;
  UseI1( AI2 ); // use the inherited interface
end;

如果您希望示例类输出I1,那么您必须更改方法

function T12.MeAsI1: I1;
var
  LI2 : I2;
begin
  LI2 := Self;
  Result := LI2;
end;

但你必须小心。考虑一个实现I1I2 的类

TImplementor = class( TInterfacedPersistent, I1, I2 )
private
  procedure I1_Test1;
  procedure I2_Test1;
  procedure I2_Test2;

  procedure I1.Test1 = I1_Test1;
  procedure I2.Test1 = I2_Test1;
  procedure I2.Test2 = I2_Test2;
end;

procedure TImplementor.I1_Test1;
begin
  Writeln( 'I1_Test1' );
end;

procedure TImplementor.I2_Test1;
begin
  Writeln( 'I2_Test1' );
end;

procedure TImplementor.I2_Test2;
begin
  Writeln( 'I2_Test2' );
end;

现在我们可以使用它了

var
  LImplementor : TImplementor;
begin
  LImplementor := TImplementor.Create;
  try
    UseI1( LImplementor );
    // Output
    // I1_Test1
    UseI2( LImplementor );
    // Output
    // I2_Test1
    // I2_Test2
    // I2_Test1 !!!!!
  finally
    LImplementor.Free;
  end;
end;

虽然我们调用了两次UseI1,但我们得到了两个不同的输出,因为调用了两个不同的方法。

【讨论】:

  • 我在哪里提到了I2中的重新声明
  • 我只是重新排列答案
  • A,我明白你的意思了,你可以强制对象对 I1 和 I2 继承的 I1 有不同的实现。这看起来像是造成混乱的秘诀,但我明白你的意思。
  • 谢谢,你的例子有效。但是在我的代码中,我将使用 Uwe 的解决方案。
  • @David 该类仅支持实现的接口。如果一个已实现的 - 支持的 - 接口是从另一个继承的,您也可以将其用作继承自的接口接口,但不能直接从该类继承。在这种情况下,实现 I2 不会实现继承的 I1。但是在获得 I2 之后,您也可以将其用作 I1。我的 TImplementor 示例也应该显示它与直接实现 I1 之间的区别
猜你喜欢
  • 2011-03-09
  • 1970-01-01
  • 2022-11-29
  • 1970-01-01
  • 2011-10-23
  • 2011-03-08
  • 2013-11-28
  • 2015-09-04
  • 1970-01-01
相关资源
最近更新 更多