當前位置:外匯行情大全網 - 期貨行情 - 如何發布delphi的tinterfacedobject?

如何發布delphi的tinterfacedobject?

了解delphi第4章界面

前不久,壹個做軟件的朋友給了我壹個謎語。謎語是“相親”,讓我猜壹個軟件術語。我想了大概壹分鐘,猜到答案是“面向對象”。我覺得很有趣,就想了壹個謎語來回答他。謎語是“吻”,讓他猜壹個軟件術語。壹分鐘後,他幽默地說:“當妳面對妳美麗的對象時,妳會情不自禁地和她說話!”。我們同時大笑起來。有說有笑,好像我們和自己節目的關系加深了。對我們來說,軟件就是生命。

第壹節界面的概念

“接口”這個詞的含義太寬泛,容易被誤解。這裏說的接口不是討論程序模塊化設計中的程序接口,更不是計算機硬件設備之間的接口。我現在要講的接口是壹種類似於類的編程語言概念,也是實現分布式對象軟件的基礎技術。

在DELPHI中,接口像類壹樣被定義,但是不使用保留的word類,而是使用接口。雖然接口和類有相似的定義,但它們的概念卻大不相同。

我們知道,類是具有相同屬性和行為的對象的抽象描述。類的描述是針對現實世界中的對象的。接口不描述對象,只描述行為。接口是壹個行為方法的描述,不管它是壹個對象還是實現這個行為方法的其他東西。所以接口和類的出發點不壹樣,只是看問題的角度不壹樣。

可以說,在跨進程或分布式編程技術的開發中,接口是壹個純粹的技術概念。類的概念是壹種普遍的思維方式,是面向對象思維的核心。然而,接口的概念確實是隨著面向對象軟件的思想發展起來的。使用接口的概念來理解和構造跨進程或分布式的軟件結構,比早期直接使用的遠程過程調用(RPC)等底層概念更加直觀和簡單。因為妳可以像理解壹個對象壹樣理解壹個接口,而妳並不關心這個對象是本地的還是遠程的。

在DELPHI中,接口被聲明為interface。命名原則是:接口用字母I命名,就像類用字母t命名壹樣,接口的聲明中只能定義方法,不能定義數據成員。因為接口只是對方法和行為的描述,並不存儲對象的屬性狀態。雖然妳可以在DELPHI中定義接口的屬性,但是這些屬性必須基於方法來訪問。

所有接口都直接或間接繼承自IUnknown。IUnknown是所有接口類型的原始祖先,在類的概念中與TObject具有相同的地位。“壹個接口繼承另壹個接口”的說法其實是錯誤的,應該說是“壹個接口擴展了另壹個接口”。接口的擴展體現了壹種“兼容”,是單壹的,絕不會出現壹個接口同時兼容兩個父接口的情況。

因為接口只描述了壹組方法和行為,而這些方法和行為的實現必須依賴於類。接口無法創建實例。沒有接口實例這種東西。只有類可以創建對象實例。但是壹個接口後面壹定有壹個對象實例,它是接口方法的實現者,接口是對象的壹組方法的引用。

從概念上講,壹個對象的類可以實現壹個或多個接口。類對接口的責任只是實現接口,不應該說類繼承了壹個或多個接口。“實現”和“繼承”這兩個詞含義不同,應該在概念上加以區分。

通常,在聲明接口時,您需要壹個可以唯壹標識接口類型的GUID標識符。接口類型將由分布在不同進程空間或計算機中的程序使用,不像類類型只在程序空間中被識別和使用。為了確保壹個接口類型可以在任何地方被唯壹地識別,有必要有壹個有效的方法來識別不同的接口。用人工命名的方法是不可能的,誰也不能保證妳開發的接口不會和別人同名。於是,壹個所謂的“全球唯壹標識符”GUID(global Unique Identifier)應運而生。它是由復雜算法隨機生成的標識符,長度為16字節,可以保證世界上任何地方生成的標識符都是不同的。在DELPHI的編輯環境中,妳可以很容易地生成壹個GUID標識符,用Ctrl+Shift+G作為界面的唯壹標識符。

有必要為接口指定GUID。雖然不指定接口的GUID也可以壹直編譯,但是在使用壹些與接口標識和轉換相關的函數時會出現問題。尤其是在基於COM的程序開發中,GUID壹定是不可或缺的。

接口的概念其實很簡單,但它在分布式軟件開發中起著關鍵作用。有些朋友覺得接口比較復雜,主要是不了解接口的概念和原理。因為人對自己不知道的東西總是有壹種神秘感。這種神秘感往往會讓人對未知的世界產生恐懼。要揭開界面的奧秘,就要不斷學習和理解界面的奧秘。其實探索的過程中也會有很多樂趣吧?

第二節我不知道

因為IUnknown是所有接口的共同祖先,所以妳必須先了解它。知道事情的起因,可以有效的幫助我們了解事情的過程和結果。IUnknown的原始定義在System.pas單元中。因為是在System.pas單元中定義的,所以壹定是和系統或者編譯器相關的原物。看看IUnknown的定義,很簡單,總共只有六行。

IUnknown =接口

[' { 000000000-0000-0000-C000-00000000046 } ']

函數QueryInterface(常量IID:TGUID;out Obj):HResult;stdcall

function _ AddRef:Integer;stdcall

function _Release:整數;stdcall

結束;

然而,這六行定義代碼可以成為界面世界的基礎。其中三個接口方法包含了簡單而深刻的哲學,理解這些哲學在編寫基於接口的程序時會讓我們受益匪淺。

IUnknown的三個接口方法是每個接口對象類都必須實現的方法,是接口機制的基本方法。為什麽這三種方法是接口機制的基礎?聽聽我的漫漫征途。

首先說壹下QueryInterface方法。我們知道壹個對象類可以實現多個接口。任何接口對象都必須實現IUnknown接口。所以只要得到壹個接口指針,就壹定可以通過這個接口指針調用QueryInterface方法。並調用QueryInterface了解這個接口指針還實現了哪些接口。這對接口編程機制非常重要。判斷接口指針是否實現接口功能,接口匹配,不同接口類型之間的轉換都與QueryInterface方法有關。

QueryInterface有兩個參數和壹個返回值。第壹個參數是接口類型的標識,即16字節的GUID標識。由於DELPHI編譯器知道每個接口對應什麽GUID,所以可以直接使用ImyInterface這樣的標識符作為第壹個參數。如果接口支持第壹個參數指定的接口類型,則通過第二個參數Obj將獲得的接口指針發回調用程序,返回值為S_OK。

從這裏也可以看出為什麽要為接口指定GUID標識。因為QueryInterface方法需要這樣的標識,而且是接口和匹配轉換機制的基礎。

接下來,我們來談談_AddRef和_Release極坐標方法。_AddRef和_Release接口方法是每個要連接的對象類都必須實現的方法。_AddRef是增加接口對象的引用計數,_Release是減少接口對象的引用。如果接口對象的引用計數為零,則銷毀該接口對象並釋放空間。這是接口機制要求的壹個基本原理,就像1+1=2這樣簡單的道理,不需要深刻的解釋。數學家們會對研究為什麽壹加壹等於二感興趣。但是數學家對1+1=2有透徹的理解。同樣,深入了解接口對象引用機制,會讓我們明白很多道理,對我們的開發工作帶來好處。

有位大師曾經說過:接口都算引用!

要理解這句話,首先要理解“引用”這個概念。“引用”是“借用”的意思,表示引用關系。被報價方只有找到被報價方的人脈,被報價方才是真正的中心。因為可以通過這個引用關系找到對象,所以引用實際上是對象的身份代表。在編程中,引用實際上是壹個指針,它使用對象的地址作為對象的標識代表。

在不基於接口機制的程序中,不需要管理對象的引用關系。因為非接口對象的實例都在同壹個進程空間,所以可以通過程序嚴格控制對象的建立、使用和釋放過程。但是,在基於接口機制的程序中,對象的創建、使用和釋放可能出現在同壹個進程空間,也可能出現在不同的進程空間,甚至出現在互聯網上相隔千裏的兩臺計算機中。在壹個地方建立壹個接口,可能實現這個接口的對象存在於另壹個地方;壹個接口在壹個地方建立後,可能在另壹個地方使用。在這種情況下,使用傳統的程序來控制對象的創建和釋放是非常困難的。必須有另壹種商定的機制來處理對象的創建和釋放。所以這個重任就落在了IUnknown的_AddRef和_Release身上。

這種接口對象引用機制要求接口對象的建立和釋放由對象實例所在的程序負責,即由實現接口的對象類負責。每當在任何地方引用對象的接口時,都必須調用接口的_AddRef方法。當對象不再被引用時,還必須調用接口的_釋演法。壹旦對象實例發現它在任何地方都不再被引用,它就會釋放自己。

_AddRef和_Release方法就是為了解決接口對象實例空間管理的問題,成為所有接口對象類必須實現的方法。

第三節界面對象的生死

乍壹看,這壹節的標題似乎有點嚇人。界面對象怎麽會和生死聯系在壹起?接口對象的生死真的那麽重要嗎?壹個好的統治者應該關心人民的生死。同樣,壹個好的程序員也應該關心對象的生死。而接口對象是分布式網絡中的流浪者,我們更應該關心他們的生死!

因為接口對象隨著接口引用的生成而建立,隨著接口引用的結束而消亡。在DELPHI中使用interface,似乎沒人關心實現接口的對象是怎麽來的,怎麽死的。這就是DELPHI使用接口的簡潔性,也是它在解決使用接口機制問題時所追求的目標。當需要壹個接口的時候,總會有壹個對象為她而生。壹旦不再引用任何壹個接口,這個對象就會無怨無愛的死去,永遠不會拖累壹個字節的系統資源。有點“春天的蠶會壹直織到死,每晚蠟燭會把燈芯哭掉”。

因為接口對象的生死直接關系到引用該對象的接口的數量,所以研究在什麽情況下會增加和減少接口引用是理解接口對象生死的關鍵。

現在讓我們實現最簡單的接口對象類TIntfObj,它只實現了IUnknown接口中定義的三個基本方法。有朋友壹看就知道這個類其實是抄襲了DELPHI中TInterfacedObject類的部分代碼。只是我們分別在_AddRef和_Release方法中加入了壹些信息輸出語句,這樣就可以探討接口對象的生死問題了。請查看以下程序:

程序ProgramA

使用

系統、對話框;

類型

TIntfObj = class(to object,IUnknown)

保護

FRefCount:整數;

函數QueryInterface(常量IID:TGUID;out Obj):HResult;stdcall

function _ AddRef:Integer;stdcall

function _Release:整數;stdcall

結束;

函數TIntfObj。query interface(const IID:TGUID;out Obj):HResult;stdcall

常數

e _ no interface = HResult($ 80004002);

開始

if GetInterface(IID,Obj)then Result:= 0 else Result:= E _ no interface;

結束;

函數TIntfObj。_ AddRef:Integer;stdcall

開始

INC(fref count);

ShowMessage(Format('將引用計數增加到%d . ',[fref count]));

結果:= FRefCount

結束;

函數TIntfObj。_Release:整數;stdcall

開始

DEC(fref count);

if FRefCount & lt& gt那麽0

ShowMessage(格式('將引用計數減少到%d . ',[FRefCount]))

否則開始

破壞;

ShowMessage('將引用計數減少到0,並銷毀該對象。);

結束;

結果:= FRefCount

結束;

定義變量

ao object:TIntfObj;

aInterface:I unknown;

IntfObjLife過程;

開始

ao object:= TIntfObj。創建;

aInterface:= a object;//添加引用

aInterface:= nil;//減少壹次引用

結束;

開始

IntfObjLife

結束。

我們需要使用單步調試函數來研究接口引用計數的增減與接口生死的關系。所以建議妳在選項中清除編譯器頁面的優化項,避免編譯器對我們需要的指令進行優化。

程序執行IntfObjLife子程序的三行代碼時,請壹步壹步調試代碼。妳會發現,當壹個接口類型變量賦值發生時,會引起接口引用計數的增加或減少。

執行語句

aInterface:= a object;

消息“引用計數增加到1。”將會出現,表示已經添加了壹次接口引用。

並執行該語句

aInterface:= nil;

"引用計數減少到0,並銷毀對象."會出現,表示接口引用降為零,接口對象被刪除。

所以我們可以得出壹個結論,當給接口類型的變量賦值引用值時,接口對象的引用計數會增加;當接口類型變量的引用值被清除(賦值為nil)時,接口對象的引用計數將會減少。

看壹下下面的代碼,加深我們對這個結論的理解。

定義變量

ao object:TIntfObj;

InterfaceA,InterfaceB:I unknown;

……

ao object:= TIntfObj。創建;

interface a:= a object;//引用增加到1

interfaca:= interfaca;//引用增加到2,但馬上減少到1。

InterfaceB:= InterfaceA;//引用增加到2

inter faca:= nil;//引用減少到1

InterfaceB:= InterfaceA;//將引用減少到0並釋放對象。

……

是否把接口對象賦給變量,把接口變量賦給接口變量,把nil賦給接口變量都印證了這個結論。有趣的是,當InterfaceA := InterfaceA這句話被執行時,接口對象的引用先是增加,然後立即減少。為什麽會這樣?留給自己去想吧!

然後,讓我們看看下面的代碼:

IntfObjLife過程;

定義變量

ao object:TIntfObj;

aInterface:I unknown;

開始

ao object:= TIntfObj。創建;

aInterface:= a object;

結束;

這個過程與前壹個過程的不同之處在於,變量被定義為局部變量,最後接口變量沒有被賦予零值。我們發現,在程序運行到子例程end語句之前,接口對象的引用被減少到0並被釋放。這是為什麽呢?

我們知道,變量是有範圍的。全局變量的作用域在程序中的任何地方,而局部變量的作用域只在相應的子程序中。壹旦變量離開了它的作用域,變量本身就不存在了,它存儲的值就更沒有意義了。因此,當程序即將離開子程序時,局部變量aInterface將不再存在,其存儲的接口對象引用值也將失去意義。Smart DELPHI自動減少接口對象的引用計數,保證程序在逐層調用和返回中正確管理接口對象的內存空間。

因此,我們可以得出壹個新的結論:當任何壹個接口變量超出其作用域時,相關接口對象的引用計數都會減少。

需要註意的是,子程序的參數變量也是變量,它的作用域也在子程序的作用域內。用接口類型參數調用子程序時,相關接口對象的引用計數會因參數的傳遞而增加,子程序返回時會減少。

同樣,如果子程序的返回值是接口類型,那麽返回值的範圍就是從主調用程序的返回點到主調用程序的end語句的範圍。這種情況也會導致接口對象的引用計數增加或減少。

該總結對象的生死了。最後得出結論後,我們可以得出以下原則:

1.當把接口對象的引用值賦給全局變量、局部變量、參數變量和返回值時,接口對象的引用計數肯定會增加。

2.在變量的原始接口引用值改變之前,其關聯對象的引用計數將減少。給變量賦值nil是賦值和修改接口引用的特例,只減少了原接口對象的引用計數,不涉及新的接口引用。

3.存儲接口引用值的全局變量、局部變量、參數變量、返回值等元素,在超出其作用域時,會自動減少接口對象的引用計數。

4.當接口對象的引用計數為零時,接口對象的內存空間被自動釋放。(在壹些采用對象緩存技術的中間件系統中,比如MTS,可能不遵循這個原則。)

需要提醒妳的是,壹旦把已經建立的接口對象交給接口,對象的生死就托付給了接口。就像把妳的寶貝女兒嫁給壹個老實人,妳要完全信任他,相信他能照顧好她。從此,與對象的接觸應該是通過界面,而不是直接與對象打交道。要知道,繞過女婿,直接插手女兒的事情,可能會出大問題。不信,我們來看看下面的代碼:

計劃生育;

類型

IHusband =接口

函數get something:string;

結束;

TWife = class(TInterfacedObject,IHusband)

私人的

f something:string;

公眾的

構造函數Create(Something:string);

函數get something:string;

結束;

構造器TWife。創建(某物:字符串);

開始

繼承創建;

f Something:=某物;

結束;

函數TWife。get something:string;

開始

結果:= FSomething

結束;

程序husbanddo(aHusband:IHusband);

開始

結束;

定義變量

the life:tw ife;

the husband:IHusband;

開始

the life:= TWife。創造(‘巨富’);

TheHusband:= the life;//對象TheWife被委托給通用接口變量TheHusband。

丈夫:=零;//清除接口引用,對象消失。

生活。GetSomething//直接訪問對象肯定有錯誤!

the life:= TWife。創造(‘巨富’);

丈夫的行為(the life);//對象委托給參數接口變量aHusband,對象返回時消失。

生活。GetSomething//直接訪問對象肯定有錯誤!

結束。

  • 上一篇:南華期貨員工可以經商辦企業嗎
  • 下一篇:期貨預挖掘高點
  • copyright 2024外匯行情大全網