接口继承只不过是语法糖吗?

时间:2014-07-27 14:15:26

标签: delphi inheritance interface

以下代码不起作用。

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);
   ....

2 个答案:

答案 0 :(得分:4)

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

关于你的例子:由于I2是从I1继承而来的,你可以直接从AI2调用I1的方法,也可以在I1接口的任何地方传递AI2。

答案 1 :(得分: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,但我们得到两个不同的输出,因为调用了两种不同的方法。