首页 > 解决方案 > 我不知道我是否需要或如何实现接口的这 3 个基本方法

问题描述

我想实现一个接口,但我不能从 声明它TInterfacedObject,在这种情况下,文档说我必须实现QueryInterface, _AddRef, _Release。但我不确定这是否是强制性的。到目前为止,我还没有真正使用过接口......他们谈到了 COM 对象,但我什至不知道那是什么意思。我从来没有故意使用 COM 对象,也许只有当它们包含在我从互联网上获取的一些代码中时。我尝试了一个示例(见下文),它在没有实现这 3 种方法的情况下工作。所以...

  1. 我怎么知道我是否必须实施它们?
  2. 如果我必须实施它们,我该怎么做?我对那些方法应该做的一无所知。我应该从复制实现TInterfacedObject吗?(事实上​​,它有不止3个......那些额外的也必须复制?)

谢谢 !

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type

  IShellInterface = interface
  ['{55967E6B-5309-4AD0-B6E6-739D97A50626}']
   procedure SetPath(const APath: String);
   function GetPath: String;
  end;

  TDriveBar = class(TCustomPanel, IShellInterface)
  private
   FDriveLink: IShellInterface;
   FPath: String;
  public
   procedure SetPath(const APath: String);
   function  GetPath: String;
   property  DriveLink: IShellInterface read FDriveLink write FDriveLink;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    DriveBar1, DriveBar2: TDriveBar;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TDriveBar.SetPath(const APath: String);
begin
 FPath:= APath;
 Caption:= APath;
end;

function TDriveBar.GetPath: String;
begin
 Result:= FPath;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 ReportMemoryLeaksOnShutdown:= True;

 DriveBar1:= TDriveBar.Create(Form1);
 DriveBar1.Parent:= Form1;
 DriveBar1.SetBounds(20, 20, 250, 40);
 DriveBar1.SetPath('Drive Bar 1');

 DriveBar2:= TDriveBar.Create(Form1);
 DriveBar2.Parent:= Form1;
 DriveBar2.SetBounds(20, 80, 250, 40);
 DriveBar2.SetPath('Drive Bar 2');

 DriveBar1.DriveLink:= DriveBar2;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 DriveBar1.DriveLink.SetPath('D:\Software\Test');
 Caption:= DriveBar2.GetPath;
end;

end.

标签: delphiinterfacedelphi-10.3-rio

解决方案


任何支持接口的类都必须实现所有三种方法:

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

如果您继承的基类实现了这些方法,那么您不需要实现它们。如果基类没有它们,您的代码将无法编译。

不同的类对这些方法和主要方法有不同的实现,最重要的区别是是否启用了自动引用计数。

如果启用了引用计数,则应始终将此类的实例存储在接口引用中(变量类型必须为接口),如果禁用,则可以使用对象引用。

如果启用 ARC 将自动管理此类实例的内存,如果禁用,则需要手动释放此类实例。

例如,启用引用计数的TInterfacedObject类是和在大多数情况下禁用它的类是TComponent(除非它用作 COM 对象容器)。

基本上,在最简单的情况下,如果_AddRefand_Release只是返回,-1那么引用计数将被禁用。如果您需要实现引用计数,那么复制代码或继承自的最佳模板是TInterfacedObject.

目的是为支持的 GUIDQueryInterface添加功能和查询接口的功能。Support如果您不确定要放TInterfacedObject什么,无论您是否启用了 ARC,您都可以始终使用实现。

根据传递给构造函数的相同值,也可以有可以禁用或启用 ARC 的类。此类的示例是TXMLDocumentifnil作为启用 ARC 传递Owner,否则 ARC 被禁用。


在您的示例中,TCustomPanel继承自TComponentthat 已经实现了这些方法,因此您不必做任何事情。


推荐阅读