Delphi7 接口

@[TOC]

第七章 接口

type
  接口名称 = Interface(父接口的名称)//一般是没有的
  [GUID]//Ctrl+Alt+G自动生成,是唯一的,如果相同,则表示这两个接口完全一致
    //成员列表
  end;

注:Interface不能声明为局部类型;

GUID虽然可以省略,但是建议不要省略;

接口的成员只能是方法和属性,属性也只能通过方法读写;

接口中没必要也不允许指定任何访问权限;

声明属性时不能指定stored,default,nodefault,但数组属性可以指定default;

不存在接口类型的对象所以接口中不能含有构造和析构函数;

接口不能实例化,其中的方法不存在动态或静态绑定,所以方法后不能加关键词:

virtual,dynamic,abstract,override;

一个类可以继承多个接口,逗号隔开;

类继承接口时,父类名称不能省略,要写在第一位;

类继承一个接口就要实现这个接口所有的方法和属性;

接口实现举例

type
  I1 = Interface
    procedure F1;
  end;
  I2 = Interface
    procedure F2;
  end;
  T2 = class(TObject, I1, I2)
    procedure F1;
    procedure F2;
  end;

所有接口继承自根接口IInterface,其中最基本的方法由类TInterfacedObject实现,在System.pas;

TInterfacedObject = class(TObject, IInterface)
...
end;

当我们调用接口时,可以继承这个类而非TObject作为父类,可以省掉基本方法的实现;

实现接口中的属性举例

type
  I1 = Interface
    procedure SetX(value: Integer);
    function GetX: Integer;
    property P1: Integer read GetX write SetX;
  end;
  T1 = class(TInterfacedObject, I1);
    strict private
      I: Integer;
      procedure SetX(value: Integer);
      function GetX: Integer;
    published
      property P1: Integer read GetX write SetX;
    end;
  function T1.GetX: Integer;
  begin
    Result := self.I;
  end;
  procedure T1.SetX(value: Integer);
  begin
    Self.I := value;
  end;
  var
    obj: T1;
  begin
    obj := T1.Create;
    obj.P1 := 78;
    Writeln(obj.P1);
    FreeAndNil(obj);
    Readln;
  end.

I用于存储P1的值;

在子类中改变父类中实现的接口举例

  type
    I1 = Interface
      procedure F1;
    T1 = class(TInterfacedObject, I1);
      procedure F1;
    end;

T1的派生类T2需要以一种不同的方式来重载实现I1,只需要让T2继承I1即可;

  T2 = class(T1, I1)
    procedure F1;
  end;
  
  procedure T1.F1;
  begin
    Writeln('T1.F1');
  end;
  
  procedure T2.F1;
  begin
    Writeln('T2.F1');
  end;
  
  var
    Interface1: I1;
  begin
    Interface1 := T1.Create;
    Interface1.F1;
    Interface1 := T2.Create;
    Interface1.F1;
  end.

注:T2中的I1接口完全隐藏了T1中的I1接口,如果在T1中给I1接口的方法声明了别名,在T2中将失效;

7.4方法别名

主要是解决方法名称相同的问题;

举例

type
  I1 = Interface
    procedure SetX(value: Integer);
    function GetX: Integer;
    property P1: Integer read GetX write SetX;
  end;
  I2 = Interface
    procedure SetX(value: Integer);
    function GetX: Integer;
    property P2: Integer read GetX write SetX;
  end;
  T1 = class(TInterfacedObject, I1, I2)
  strict private
    I, J: Integer;
    procedure I1.SetX = set1;            //other name
    procedure I1.GetX = get1;
    procedure I2.SetX = set2;
    procedure I2.GetX = get2; 
  public
    procedure Set1(value: Integer);      //change
    function Get1: Integer;
    procedure Set2(value: Integer);
    function Get2: Integer;
    property p1: Integer read Get1 write Set1; //change
    property p2: Integer read Get2 write Set2; 
  end;

别名只是方法的另一个名字,并不能改变方法除了名字之外的任何信息;

7.5接口的代理

如果在T1中继承I1接口,而T0中已经实现了I1接口,可以通过以下方式直接使用

type
  I1 = Interface
    procedure F1;
  end;
  T0 = class(TInterfacedObject, I1)
    procedure F1;
  end;
  T1 = class(TInterfacedObject, I1)
  strict private
    FInterface: T1;
  public 
    property P1: I1 read FInterface write FInterface Implements I1;
  end;
procedure T0.F1;
begin
  Writeln('this is T0.F1');
end.
var
  Interface1: I1;
  obj: T1;
begin
  obj := T1.Create;
  obj.P1 := T0.Create;
  Interface1 := obj;
  Interface1.F1;
  Readln;
end.

步骤:

1.在类中声明接口类型的属性;

2.把其他实现了接口的对象赋给这个属性;

3.通过属性中存储的对象调用这个接口的功能;

以上是通过接口作为属性的类型,还可以使用类类型的属性;

T1 = class(TInterfacedObject, I1)
  strict private
    FInterface: T0;  //change
  public
    //change
    property P1: T0 read FInterface write FInterface Implements I1;
end;
procedure T0.F1;
begin
  Writeln('T0.F1');
end;
var
  Interface1: I1;
  obj: T1;
begin
  obj := T1.Create;
  obj.P1 := T0.Create;
  Interface1 := obj;
  Interface1.F1;
  Readln;
end.

这种方式成为对象代理;

7.6接口的赋值和转型

非接口变量中只有变体变量可以接受接口类型的值;

变体变量第三章;

将一个IDispatch类型的接口值赋给变体变量时,变体变量的类型码的值是varDispatch;

其他任何接口值赋给变体变量时,类型码均为varUnknown;

非接口变量值赋给接口变量:变体变量值赋给接口变量,nil赋给接口,类的对象赋给接口;

当变体变量的类型码是varUnknown时,它可以当成值赋给IInterface(IUnknown)类型的变量;

类型码是varDispatch或varEmpty时,既可以赋给IInterface(IUnknown)类型的接口变量,也可以赋给IDispatch类型的接口变量;

其他任何类型都不能赋给接口类型,任何类型也不能接口变体值;

nil可以作为值赋给任何接口类型的变量;

如果将对象赋给某个接口类型的变量,则此对象必须实现了此接口本身,即使是实现了此接口的子代接口也不行;

举例

type
  I1 = Interface(IInterface)
  End;
  I2 = Interface(I1)
  End;
  T1 = class(TInterfacedObject, I2)
  end;
var
  obj: T1;
  Interface1: I1;
  Interface2: I2;
begin
  obj := T1.Create;
  Interface1 := obj;
  //that is wrong
  Interface2 := obj;
  FreeAndNil(obj);
end.

obj实现了I2接口,它不能作为值赋给I2之外的任何接口类型的变量;

派生的接口值赋给祖先接口变量;

也只有这种情况可以将接口变量值赋给接口变量;

不过这基本没有什么意义,举例

type
  I1 = Interface
    procedure M1;
  end;
  I2 = Interface(I1)
    procedure M2;
  end;
  T1 = class(TInterfacedObject, I2)
    procedure M1;
    procedure M2;
  end;
var
  obj: T1;
  Interface1: I1;
  Interface2: I2;
begin
  obj := T1.Create;
  Interface2 := obj;
  Interface2.M2;
  Interface1 := Interface2;//here
  Interface1.M1;
  Readln;
end.

这里相当于

Interface1 := obj;

type
  I1 = Interface
  end;
  T1 = class(TInterfacedObject, I1)
  end;
var
  obj: T1;
  Interface1: I1;

然后可以

Interface1 := obj;
obj := T1(Interface1);

这种转换是一种隐匿转换,某些时候不能确保安全性;

应当使用as操作符;

举例

type
  T1 = Interface
  ['{D8C36ABA-FCF9-46F1-A55B-8E69EEA75244}']
    procedure M1;
  end;
  T1 = class(TInterfacedObject, I1)
    procedure M1;
    procedure M2;
    procedure M3;virtual;
  end;
  
procedure T1.M1;
begin
  Writeln('T1.M1');
end;

procedure T1.M2;
begin
  Writeln('T1.M2');
end;

procedure T1.M3;
begin
  Writeln('T1.M3');
end;
var
  Interface1: T1;
  obj: T1;
begin
  obj := T1.Create;
  Interface1 := obj as T1;
  Interface1.M1;
  Readln;
end.

注:使用as转型必须加上GUID,否则无法编译;

begin
  obj := T1.Create;
  Interface1 := obj as T1;
  // three more code
  obj := nil;
  obj := Interface1 as T1;
  obj.M3;
  //end more
  Interface1.M1;
  Readln;
end.

首先将obj设置成nil,将Interface1转为T1类型的引用赋给obj,调用M3;

注意到M3是虚方法,这就解除了M3和obj之间的静态绑定;

还记得静态绑定吗

接口的生存期受系统自动管理,无需手动销毁;

通过接口实现多态举例

unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm3 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  IGreed = Interface
    procedure Greed;
  End;
  TCHGreen = class(TInterfacedObject, IGreed)
    procedure Greed;
  end;
  TJPGreen = class(TInterfacedObject, IGreed)
    procedure Greed;
  end;
  TENGreen = class(TInterfacedObject, IGreed)
    procedure Greed;
  end;
var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TCHGreen.Greed;
begin
  ShowMessage('zao');
end;

procedure TJPGreen.Greed;
begin
  ShowMessage('shang');
end;

procedure TENGreen.Greed;
begin
  ShowMessage('hao');
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  obj: IGreed;
begin
  obj := TCHGreen.Create;
  obj.Greed;
end;

procedure TForm3.Button2Click(Sender: TObject);
var
  obj: IGreed;
begin
  obj := TJPGreen.Create;
  obj.Greed;
end;

procedure TForm3.Button3Click(Sender: TObject);
var
  obj: IGreed;
begin
  obj := TENGreen.Create;
  obj.Greed;
end;

end.

这里各个类没有继承TGreed而是实现了IGreed接口;

调用方法通过接口调用;

所有的对象都无需手动销毁;

由于接口的生存周期由系统自动管理,所以它指向的实体也会由系统自动销毁,无需手动销毁。