基於Delphi的介面編程入門

來源:互聯網
上載者:User

標籤:編譯器   function   ack   facet   執行   ram   and   pos   begin   

為什麼使用介面?

  舉個例子好了:有這樣一個賣票服務,電影院可以賣票,歌劇院可以賣票,客運站也可以賣票,那麼我們是否需要把電影院、、歌劇院和客運站都設計成一個類架構以提供賣票服務?要知道,連經理人都可以賣票,很顯然不適合把經理人也包括到賣票服務的繼承架構中,我們需要的只是一個共通的賣票服務。於是,賣票的服務是個介面,電影院、歌劇院什麼的只要都遵循這樣一個服務定義就能很好地相互互動和溝通(如果需要的話)。

  如何在Delphi中使用介面

  1、聲明介面

IMyInterface = interface(IInterface) //說明(1)
[‘{63E072DF-B81E-4734-B3CB-3C23C7FDA8EA}‘] //說明(2)
function GetName(const str: String): String; stdcall;

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; //說明(3)
function _AddRef: Integer; stdcall; //使介面引用數加1。
function _Release: Integer; stdcall;//使介面引用數減1,當小於等於0時作釋放動作。
end;

  說明(1):如果有繼承關係則在括弧裡填父介面,否則省卻,如:IMyInterface = interface這樣就行。

  說明(2):此GUID可選,如果要實現具有COM特性的介面的話則需要加上,Delphi中對於有GUID的介面在運行時在VMT表的預定位置產生介面的資訊,如介面方法的定義、方法參數定義的詳細資料。

  說明(3):介面必須實現這三個函數。

  2、介面的實現

  介面服務是由類來實現的。

TIntfClass = class(TObject, IMyInterface)
private
 FCounter: Integer;
 FRefCount: Integer;
public
 function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
 ...
end;

  3、擷取介面

    a. 使用類型轉換。 如:

       var aIntf: IMyInterface;
       begin
         aObj := TIntfClass.Create;
          try
           aIntf := (IMyInterface(aObj);
           ...

  b. 利用Delphi編譯器內建機制。 如:aIntf := aObj。

  c. 利用對象的QueryInterface方法。如OleCheck(aObj.QueryInterface(IID, aIntf)); 只能存取有GUID的COM介面。

  d. 利用as操作符。

  使用as操作符必須符合下麵條件:

  1.介面必須明確地指定是從IInterface介面繼承下來。

  2.必須擁有GUID值

  在Delphi7中介面的實作類別還必須是從TInterfacedObject繼承下來才行,如:

       TIntfClass = class(TInterfacedObject, IMyInterface)

  4、介面和對象生命期

  因為Delphi會自行檢查介面,如果在使用後沒有釋放而在產生的程式裡加上釋放代碼,但也因這樣帶來了問題,如下面代碼:

var
 i: Integer;
 aObj: TIntfClass;
 aIntf: IMyInterface;
begin
 aObj := TIntfclass.Create;
 try
  aIntf := aObj;
  aIntf.GetName...
 finally
  aIntf := nil;
  FreeAndNil(aObj);
end;

  上面的代碼執行的話會產生存取違規錯誤,是因為對介面置nil時已釋放介面,而FreeAndNil(aObj)會再釋放aIntf一次,而在對aIntf置
nil時已釋放了該對象。解決這個問題只要不讓介面幹擾對象的生命期就可以了,在Release中只需減引用計數而不做釋放的動作。

function TIntfClass._Release: Integer;
begin
     Result := InterlockedDecrement(FRefCount);
end;

  5、介面的委託(Interface Delegation)

  分為兩種:

  1. 對象介面委託

  2. 類對象委託。

  . 對象介面委託,假如已有下面介面定義:

IImplInterface = interface(IInterface)
function ConvertToUSD(const iNTD: Integer): Double;
function ConvertToRMB(const iNTD: Integer): Double;
end;

  接著有一個類實現了該介面:

TImplClass = class(TObject, IImplInterface)
private
 FRefCount: Integer;
public
 function ConvertToUSD(const iNTD: Integer): Double;
 ...
end;

implementation

function TImplClass.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
 Result := 0
else
 Result := E_NOINTERFACE;
end;

function TImplClass._Release: Integer;
begin
 Result := InterlockedDecrement(FRefCount);
if Result = 0 then
 Destroy;
end;
... ...

  現在有另外一個類TIntfServiceClass要實現IImplInterface介面,不用重新定義,只須使用上面的TImplClass就可以:

TIntfServiceClass = class(TObject, IImplInterface)
private
 FImplService: IImplInterface;
 //FSrvObj: TImplClass; //如果是用類對象委託的話
public
 Constructor Create; overload;
 Destructor Destroy; override;
 Constructor Create(aClass: TClass); overload;
 property MyService: IImplInterface read FImplService implements IImplInterface;
 // property MyService: TImplClass read FSrvObj implements IImplInterface; //如果是用對象委託的話。
end;

  實現如下:

constructor TIntfServiceClass.Create;
begin
 FImplService := TImplClass.Create;
end;

constructor TIntfServiceclass.Create(aClass: TClass);
var
 instance: TImplClass;
begin
 instance := TImplClass(aClass.NewInstance);
 FImplService := instance.Create;
end;

destructor TIntfServiceClass.Destroy;
begin
 FImplService := nil; //遵照TImplClass使用引用計數來控制對象生命週期,看TImplClass的Destroy實現。
 inherited;
end;

  6、介面和RTTI

  Delphi中在VMT-72位移處定義了介面哥格指標:vmtIntfTable = -72。

  相關函數:

GetInterfaceCount; //擷取介面數量。
GetInterfaceTable; //擷取介面表格。

  相關結構:

TInterfaceEntry = packed record
IID: TGUID;
VTable: Pointer;
IOffset: Integer;
ImplGetter: Integer;
end;

PInterfaceTable = ^TInterfaceTable;
TInterfaceTable = packed record
EntryCount: Integer;
Entries: array[0..9999] of TInterfaceEntry;
end;

  Self是指向VMT指標的指標,所以:Self.GetInterfaceTable.EntryCount等價於:

aPtr := PPointer(Integeer((Pointer(Self))^) + vmtIntfTable)^;

  只要在聲明中使用M+/M-指令就能在Delphi中編譯出的程式裡添加RTTI資訊,如:

{$M+}
iInvokable = interface(IInterface)
{$M-}

  介面的RTTI資訊由TIntfMetaData記錄結構定義:

TIntfMetaData = record
name: String; //介面名稱
UnitName: String; //介面聲明的程式單元名稱
MDA: TIntfMethEntryArray; //儲存介面中方法資訊的動態數組
IID: TGUID; //介面的GUID值
Info: PTypeInfo; //描述介面資訊的指標
AncInfo: PTypeInfo; //描述父代資訊的指標
NumAnc: Integer; //此介面繼承自父代介面的方法數目
end;

  TIntfMethEntryArray的定義如下:

type
 TCallConv = (ccReg, ccCdecl, ccPascal, ccStdCall, ccSafeCall);
 TIntfMethEntry = record
 Name: String; //方法名稱
 CC: TCallConv; //調用慣例
 Pos: Integer; //方法在介面中的位置
 ParamCount: Integer; //方法的參數數目
 ResultInfo: PTypeInfo; //描述方法回傳類型的資訊指標
 SelfInfo: PTypeInfo; //描述方法本身的資訊指標
 Params: TIntfParamEntryArray; //描述參數資訊的動態數組
 HasRTTI: Boolean; //這個方法是否擁有RTTI資訊的布爾值
end;

TIntfMethEntryArray = array of TIntfMethEntry;

  參數資訊TIntfParamEntry定義:

TIntfParamEntry = record
Flags: TParamFlags;
Name: String;
Info: PTypeInfo;
end;

TTypeInfo = record
Kind: TTypeKind; //資料類型
Name: ShortString; //類型資訊的字串格式
end;

基於Delphi的介面編程入門

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.