Interface理解

代码:

  oIntfA, oIntfB: IInterface;

  oEntA ,oEntB:IElement;

  //根据控件的下标来获Pointer对应的接口;

  oIntfA := IInterface(Pointer(PropA.Items.Objects[ItemIndex]));

  oIntfB := IInterface(Pointer(PropB.Items.Objects[ItemIndex]));

  //转为相应的接口

  if Supports(oIntfA, IElement) then

  begin

    oEntA := (oIntfA as IElement);

    oEntB := (oIntfB as IElement);

    if oEntA.ElementType = oEntB.ElementType then

    begin

       ........

    end;

end;

接口是什么?

  IInterface = interface

    ['{00000000-0000-0000-C000-000000000046}']

    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

  end;

['{00000000-0000-0000-C000-000000000046}']这是其实是接口的唯一标识,也就是我们说的TGUID;当把接口注册给系统后,我们可以通过注册表检索到00000000-0000-0000-C000-000000000046这样的键值。那么这就意味着,我们只需要知道一个TGUID的值就可以方便的访问这个接口。

关于变量:你可以在接口中定义其他的方法,但是Delphi中是不允许给接口添加变量成员的,因为接口是不允许有实现部分的。

关于继承:    同样,接口也可以继承、封装以及方法的覆盖。继承接口同类继承类似:

type

  INewInterface = interface(IInterface)

    // 定义一个新的接口INewInterface,并告诉编译器它是继承自IInterface接口

    ....

  end;

接口的继承大致就是这样,和TObject是所有类的根类一样,在Delphi中IInterface是所有接口的根类,那么类似于下面的继承:

type

  INewInterface = interface

    ...

  end;

其实也是定义了一个继承自IInterface的新接口INewInterface(和类名前加一个大写字母T一样,我们习惯于在接口名前加一个大写字母I,当然这只是一个命名约定)

唯一标识:接口的TGUID是每一个接口的唯一标识,那么也就是说,TGUID是不能重复的。你不能简单的从别处抄袭过来,那样是错误的。如果你需要一个TGUID,你可以在Delphi的代码编辑框中同时按下CTRL+SHIFT+G来获得一个新的TGUID值,这个值是Delphi为你自动生成的。

那么怎么让接口为我们工作呢?

其实,接口的实现是需要借助于类来完成的(当然这看上去和C++中的多重继承的写法差不多)注意,既然接口是需要借助类来实现的,那么也就是说用来实现接口的类,必须实现接口中所有已定义的方法:

type

  TNewInterfaceClass = class(TInterfacedObject, INewInterface)

    // TInterfacedObject为类名,INewInterface为我们上面定义的接口名

    ...

  end;

 

一般,我们用来实现接口的基类不会选TObject而会选TInterfacedObject,理由是TInterfacedObject类已经帮我们实现了IInterface接口中的方法,我们只需要实现我们自己接口中新的方法就可以了。

    既然我们已经通过类实现了接口中的方法,那么我们就可以使用这个接口来为我们服务了,实例化接口也非常简单:

var

  NewFace: INewInterface;

begin

  NewFace:= TNewInterfaceClass.Create(); // 创建接口

  NewFace.xxx; // 调用接口中的方法

  NewFace:= nil; // 释放

end;

有朋友可能会奇怪,接口的释放为什么只是直接赋为nil?我们前面说过了:接口即没有构造方法,也没有析构方法。既然没有析构方法,那么就意味着我们不能用释放类的方式来释放接口。

那么直接把接口对象指空会造成内存泄露吗?

 答案是否定的,因为接口提供了一个引用记数的机制:当某接口实例(也就是实现了这个接口的对象)被引用,比如被赋值给一个接口变量时,该接口实例的AddRef方法会被编译器自动调用,引用计数将增加一。引用取消时编译器则会调用_Release方法将引用计数减一。引用计数减到零时,表示已无其他接口变量引用此接口实例,此时编译器会自动释放它。

    要注意的是,接口对象与类对象是不能混用的。当然像我们上面的例子里,NewFace也只能调用INewInterface接口中所定义过的方法,而不能调用类中定义的不存在于接口中的方法。

    当然,一个类可以同时实现多个接口,多个接口彼此用逗号隔开。如:

type

  TNewInterfaceClass = class(TInterfacedObject, IInterface1, IInterface2)

  ...

  end;

 

同样,实现多个接口的类必须依次实现每个接口中定义的方法。

当两个接口中有同名方法怎么办?

为他们取别名:

type

  IInterface1 = interface(IInterface)

    // 接口1

    fucntion Func(): Boolean;

  end;

 

  IInterface2 = interface(IInterface)

    // 接口2

    function Func(): Boolean;

  end;

 

  TClasses = class(TInterfacedObject, IInterface1, IInterface2)

  public

    function IInterface1.Func: Func1;

    function IInterface2.Func: Func2;

      { 为同名方法起别名 }

    function Func1: Boolean;

    function Func2: Boolean;

      { 声明方法 }

  end;

 

委托实现

Delphi中还可以使用imploements指示符用于委托另一个类或接口来实现接口的某个方法,有时这个方法又被称为委托实现,关于implements的用法如下:

type

  TInterClass = class(TInterfacedObject, IInterface1)

    ...

    function GetClasses: TClasses;

    property Face: TClasses read GetClasses implements IInterface1;

    ...

  end;

 

上面的代码中,implements指示字会要求编译器在Face属性中寻找实现IInterface1接口的方法,属性的类型必须是一个类或一个接口。implements可以指定多个接口,彼此用逗号分隔。

    implements指示字的好处是:
    一、他允许以无冲突的方式进行接口聚合。(聚合是COM中的概念)
    二、他能够延后占用实现接口所需要的资源,直到确实需要资源。

    下面是关于委托的一个详细例子(该例在DELPHI7 + WIN2000 SP4 中调试通过):

  INewInterface = interface(IInterface)

    // 定义接口

    function SayHello: string; stdcall;

      // 接口方法

  end;

 

  TNewClass = class(TInterfacedObject, INewInterface)

  public

    function SayHello: string; stdcall;

      // 第一个类实现接口中的方法

  end;

 

  TNewClass1 = class(TInterfacedObject, INewInterface)

  private

    FNewClass: INewInterface;

  public

      // 注意,在这个类中并没有实现接口中的SyaHello方法

    constructor Create;

    destructor Destroy; override;

    property NewClass: INewInterface read FNewClass implements INewInterface;

      // 接口对象委托 如果是类对象委托应该是

    // property NewClass: TNewClass read FNewClass implements INewInterface;

  end;

 

implementation

 

 

function TNewClass.SayHello: string;

begin

  Result:= ClassName;

  ShowMessage(Result);

end;

 

 

constructor TNewClass1.Create;

begin

  inherited Create();

  FNewClass:= TNewClass.Create;

    // 在构造方法中创建接口

end;

 

destructor TNewClass1.Destroy;

begin

  FNewClass:= nil;

    // 在析构方法中释放接口

  inherited Destroy();

end;

 

调用的例子:

var

  NewInterface: INewInterface;

begin

  NewInterface:= TNewClass1.Create;

  NewInterface.SayHello;

  NewInterface:= nil;

end;

 

题外话:如果你还没有接触过COM/COM+的话,也许你会认为接口十分麻烦(PS: 当年我刚学的时候真想一脚把发明接口机制的人踹死),但是接口经过COM的封装后,将变的非常的有意义,呵呵

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值