| 導購 | 订阅 | 在线投稿
分享
 
 
 

Delphi下的接口編程學習筆記

來源:互聯網網民  2006-05-27 07:59:27  評論

1.1 爲什麽使用接口?

舉個例子好了:有這樣一個賣票服務,電影院可以賣票,歌劇院可以賣票,客運站也可以賣票,那麽我們是否需要把電影院、、歌

劇院和客運站都設計成一個類架構以提供賣票服務?要知道,連經理人都可以賣票,很顯然不適合把經理人也包括到賣票服務的繼承架構

中,我們需要的只是一個共通的賣票服務。于是,賣票的服務是個接口,電影院、歌劇院什麽的只要都遵循這樣一個服務定義就能很好地

相互交互和溝通(如果須要的話)。

1.2 如何在Delphi中使用接口

1.2.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):接口必須實現這三個函數。

1.2.2 接口的實現

接口服務是由類來實現的。

TIntfClass = class(TObject, IMyInterface)

private

FCounter: Integer;

FRefCount: Integer;

public

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

...

end;

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

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

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

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

 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
      1.1 爲什麽使用接口? 舉個例子好了:有這樣一個賣票服務,電影院可以賣票,歌劇院可以賣票,客運站也可以賣票,那麽我們是否需要把電影院、、歌 劇院和客運站都設計成一個類架構以提供賣票服務?要知道,連經理人都可以賣票,很顯然不適合把經理人也包括到賣票服務的繼承架構 中,我們需要的只是一個共通的賣票服務。于是,賣票的服務是個接口,電影院、歌劇院什麽的只要都遵循這樣一個服務定義就能很好地 相互交互和溝通(如果須要的話)。 1.2 如何在Delphi中使用接口 1.2.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):接口必須實現這三個函數。 1.2.2 接口的實現 接口服務是由類來實現的。 TIntfClass = class(TObject, IMyInterface) private FCounter: Integer; FRefCount: Integer; public function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; ... end; 1.2.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) 1.2.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; 1.2.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; 1.2.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;    
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 
 熱帖排行
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有