對於Object Pascal語言來說,最近一段時間最有意義的改進就是從Delphi3開始支持接口(interface),接口定義了能夠與一個對象進行交互操作的一組過程和函數。對一個接口進行定義包含兩個方面的內容,一方面是實現這個接口,另一方面是定義接口的客戶。一個類能實現多個接口,即提供多個讓客戶用來控制對象的“表現方式”。
正如名字所表現的,一個接口就是對象和客戶通信的接口。這個概念像C++中的PUREVIRTUAL類。實現接口的函數和過程是支持這個接口的類的工作。
在這里你將學到接口的語言元素,要想在應用程序中使用接口,請參考COM和ActiveX方面的資料;
1.定義接口
就像所有的Delphi類都派生於TObject一樣,所有的接口都派生於一個被稱為是IUnknown的接口,IUnknown在system單元中定義如下:
IDispatch = interface(IUnknown)
['{00020400-0000-0000-C000-000000000046}']
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
end;
正如你所看到的,接口的定義就像是類的定義,最根本的不同是在接口中有一個全局唯一標識符(GUID),它對於每一個接口來說是不同的。對IUnknown的定義來自於Microsoft的組件對象模型(COM)規范。
如果你知道怎樣創建Delphi的類,那么定義一個定制的接口是一件簡單的事情,下面的代碼定義了一個新的接口稱為IFoo,它包含一個被稱為F1()的方法:
type
IFoo = Interface
['{2137BF60-AA33-11D0-A9BF-9A4537A42701}']
function F1 : Integer;
end;
提示在Delphi的IDE中,按Ctrl+Shift+G鍵可以為一個接口生成一個新的GUID。
下面的代碼聲明了一個稱為IBar的接口,它是從IFoo接口繼承來的:
type
IBar = Interface(IFoo)
['{2137BF61-AA33-11D0-A9BF-9A4537A42701}']
function F2 : Integer;
end;
2.實現接口
下面的代碼演示了在一個類TFooBar中怎樣實現IFoo和IBar接口:
type
TFooBar = class(TInterfacedObject, IFoo, IBar)
function F1 : Integer;
function F2 : Integer;
end;
function TFooBar.F1 : Interger;
begin
Result := 0;
end;
function TFooBar.F2 : Interger;
begin
Result := 0;
end;
注意,一個類可以實現多個接口,只要在聲明這個類時依次列出要實現的接口。編譯器通過名稱來把接口中的方法與實現接口的類中的方法對應起來,如果一個類只是聲明要實現某個接口,但並沒有具體實現這個接口的方法,編譯將出錯。
如果一個類要實現多個接口,而這些接口中包含同名的方法,必須把同名的方法另取一個別名,請看下面的程序示例:
type
IFoo = Interface
['{2137BF60-AA33-11D0-A9BF-9A4537A42701}']
function F1 : Integer;
end;
type
IBar = Interface
['{2137BF61-AA33-11D0-A9BF-9A4537A42701}']
function F1 : Integer;
end;
TFooBar = class(TInterfacedObject, IFoo, IBar)
//為同名方法取別名
function IFoo.F1 = FooF1;
function IBar.F1 = BarF1;
//接口方法
function FooF1 : Integer;
function BarF1 : Integer;
end;
function TFooBar.FooF1 : Interger;
begin
Result := 0;
end;
function TFooBar.BarF1 : Interger;
begin
Result := 0;
end;
3.implements指示符
implements指示符是在Delphi4中引進的,它的作用是委托另一個類或接口來實現接口的某個方法,這個技術有時又被稱為委托實現,關於implements指示符的用法,請看下面的代碼:
type
TSomeClass = class(TInterfacedObject, IFoo)
//Stuff
function GetFoo : TFoo;
property Foo : TFoo read GetFoo implements GetFoo;
//Stuff
end;
在上面例子中的implements指示符是要求編譯器在Foo屬性中尋找實現IFoo接口方法。屬性的類型必須是一個類,它包含IFoo方法或類型是IFoo的接口或IFoo派生接口。implements指示符后面可以列出幾個接口,彼此用逗號隔開。
implements指示符在開發中提供了兩個好處:首先,它允許以無沖突的方式進行接口聚合。聚合
(Aggregation)是COM中的概念。它的作用是把多個類合在一起共同完成一個任務。其次,它能夠延后占用實現接口所需的資源,直到確實需要資源。例如,假設實現一個接口需要分配一個1MB的位圖,但這個接口很少用到。因此,可能平時你不想實現這個接口,因為它太耗費資源了,用implements指示符后,可以只在屬性被訪問時才創建一個類來實現接口。
4.使用接口
當在應用程序中使用接口類型的變量時,要用到一些重要的語法規則。最需要記住的是,一個接口是生存期自管理類型的,這意味着,它通常被初始化為nil,它是引用計數的,當獲得一個接口時自動增加一個引用計數;當它離開作用域或賦值為nil時它被自動釋放。下面的代碼演示了一個接口變量的生存期自管理機制。
var
I : ISomeInterface;
begin
//I被初始化為nil
I := FunctionReturningAnInterface; //I的引用計數加1
I.SomeFunc;
//I的引用計數減1,如果為0,則自動釋放。
end;
關於接口變量的另一個規則是,一個接口變量與實現這個接口的類是賦值相容的,例如,下面的代碼是合法的:
procedure Test(FB : TFooBar)
var
F : IFoo;
begin
F :=FB; //合法,因為FB支持IFoo
.
.
.
最后,類型強制轉換運算符as可以把一個接口類型的變量強制類型轉換為另一種接口。示例如下:
var
FB : TFooBar;
F : IFoo;
B : IBar;
begin
FB := TFooBar.create;
F :=FB; //合法,因為FB支持IFoo
B := F as IBar; //把F轉換為IBar