為了完全支持OLE(Object Linking and Embedding,對(duì)象連接與嵌入,簡稱OLE技術(shù)),32位Delphi 增加了Variant 數(shù)據(jù)類型,本節(jié)將從宏觀角度來分析這種數(shù)據(jù)類型。實(shí)際上,Variant類型對(duì)Pascal語言有普遍而深入的影響,Delphi 控件庫中與OLE 無關(guān)的地方也使用到這種類型。
Variant變量沒有類型
一 般說來,你可以用Variant 變量存儲(chǔ)任何數(shù)據(jù)類型,對(duì)它執(zhí)行各種操作和類型轉(zhuǎn)換。需要注意的是:這違反了Pascal 語言的一貫原則,有悖于良好的編程習(xí)慣。variant 變量的類型檢查和計(jì)算在運(yùn)行期間才進(jìn)行,編譯器不會(huì)提示代碼中的潛在錯(cuò)誤,這些錯(cuò)誤在進(jìn)一步測試中才能發(fā)現(xiàn)??傊?,你可以認(rèn)為包含variant變量的代 碼是解釋性代碼,正如解釋性代碼一樣,許多操作直到執(zhí)行時(shí)才能知道,這對(duì)代碼運(yùn)行速度會(huì)有很大的影響。
上面對(duì)Variant 類型的使用提出了警告,現(xiàn)在來看看Variant 類型究竟能干什么?;旧险f,如果聲明了一個(gè)variant 變量:
var
V: Variant;
你就可以把各種不同類型的值賦給它:
V := 10;
V := 'Hello, World';
V := 45.55;
一 旦得到一個(gè)variant 值,你可以把它拷貝給任何兼容或不兼容的數(shù)據(jù)類型。如果你把值賦給不兼容的數(shù)據(jù)類型,Delphi 會(huì)力盡所能進(jìn)行轉(zhuǎn)換,無法轉(zhuǎn)換則頒布一個(gè)運(yùn)行時(shí)間錯(cuò)誤。實(shí)際上,variant變量中不僅包含了數(shù)據(jù)還包含有類型信息,并允許一系列運(yùn)行時(shí)間操作,這些操 作很方便,但運(yùn)行速度慢且安全性差。
見例VariTest,它是上面代碼的擴(kuò)展。窗體上有三個(gè)編輯框,一對(duì)按鈕,第一個(gè)按鈕的OnClick 事件代碼如下:
procedure TForm1.Button1Click(Sender: TObject);
var
V: Variant;
begin
V := 10;
Edit1.Text := V;
V := 'Hello, World';
Edit2.Text := V;
V := 45.55;
Edit3.Text := V;
end;
很有趣是不是?你可以把一個(gè)值為字符串的variant 變量賦給編輯框Text 屬性,還可以把值為整數(shù)或浮點(diǎn)數(shù)的variant 變量賦給Text屬性。正如你在圖10.1中所看到的,一切正常。
(圖10.1)按Assign按鈕后,例VariTest的輸出結(jié)果
圖 10.1: 例 VariTest 的 Assign 按鈕 Click 事件輸出結(jié)果
更糟糕的是:你還可以用variant變量計(jì)算數(shù)值,從第二個(gè)按鈕的Click事件代碼就可看到這一點(diǎn):
procedure TForm1.Button2Click(Sender: TObject);
var
V: Variant;
N: Integer;
begin
V := Edit1.Text;
N := Integer(V) * 2;
V := N;
Edit1.Text := V;
end;
至 少這種代碼帶有一定危險(xiǎn)性,如果第一個(gè)編輯框包含了一個(gè)數(shù)字,那么一切運(yùn)行正常;如果不是,將會(huì)引發(fā)異常。這里再重申一遍,如果不到萬不得以,不要隨便使 用Variant 類型,還是應(yīng)堅(jiān)持使用傳統(tǒng)的Pascal 數(shù)據(jù)類型和類型檢查方法。在Delphi 和 VCL中,variant變量主要是用于 OLE 支持和數(shù)據(jù)庫域的訪問。
Variant類型內(nèi)部結(jié)構(gòu)
Delphi中定義了一個(gè) variant 記錄類型,TVarData,它與Variant 類型有相同的內(nèi)存布局。你可以通過TVarData訪問variant變量的實(shí)際類型。TVarData 結(jié)構(gòu)中包含了Variant類型信息(由Vtype域表示)、一些保留域及當(dāng)前值。
VType域的取值包括OLE 自動(dòng)化中的所有數(shù)據(jù)類型,這些類型通常叫OLE 類型或variant 類型。以下是variant 類型的完整列表,按字母順序排列:
varArray
varBoolean
varByRef
varCurrency
varDate
varDispatch
varDouble
varEmpty
varError
varInteger
varNull
varOleStr
varSingle
varSmallint
varString
varTypeMask
varUnknown
varVariant
你可以在Delphi 幫助系統(tǒng)的variants 主題下找到這些類型的說明。
還 有許多操作variant 變量的函數(shù),你可以用它們進(jìn)行特定的類型轉(zhuǎn)換,或通過它們獲取variant變量的類型信息(例如VarType 函數(shù)),當(dāng)你用variant變量寫表達(dá)式時(shí),Delphi會(huì)自動(dòng)調(diào)用這些類型轉(zhuǎn)換和賦值函數(shù)。另外還有操作variant 數(shù)組的例程,你可以通過幫助文件的Variant support routines 主題了解相關(guān)內(nèi)容。
Variant類型運(yùn)行很慢!
Variant 類型代碼運(yùn)行很慢,不僅數(shù)據(jù)類型轉(zhuǎn)換如此,兩個(gè)值為整數(shù)的Variant 變量相加也是如此。它們幾乎跟Visual Basic這種解釋性代碼一樣慢!為了比較Variant變量和整型變量的運(yùn)行速度,請(qǐng)看例VSpeed 。
程序中設(shè)置了一個(gè)循環(huán),記錄運(yùn)行時(shí)間并在進(jìn)程條中顯示運(yùn)行狀態(tài)。下面是基于variant類型的一段代碼,基于整型的代碼與此相似:
procedure TForm1.Button1Click(Sender: TObject);
var
time1, time2: TDateTime;
n1, n2: Variant;
begin
time1 := Now;
n1 := 0;
n2 := 0;
ProgressBar1.Position := 0;
while n1 < 5000000 do
begin
n2 := n2 + n1;
Inc (n1);
if (n1 mod 50000) = 0 then
begin
ProgressBar1.Position := n1 div 50000;
Application.ProcessMessages;
end;
end;
// we must use the result
Total := n2;
time2 := Now;
Label1.Caption := FormatDateTime (
'n:ss', Time2-Time1) + ' seconds';
end;
記 時(shí)這段代碼值得一看,因?yàn)槟憧梢园阉玫饺魏晤愋偷男阅軠y試中。正如你所看到的,程序用Now 函數(shù)獲取當(dāng)前的時(shí)間,用FormatDateTime 函數(shù)格式化時(shí)間差,輸出結(jié)果以分("n")和秒("ss")表示。除此之外,你可以用Windows API的GetTickCount 函數(shù),該函數(shù)能精確顯示操作系統(tǒng)啟動(dòng)后至當(dāng)前的毫秒數(shù)。
從上例可見兩者的速度差異非常之大,以至于不用精確記時(shí)也能看到這種差異。圖10.2是在本人計(jì)算機(jī)上運(yùn)行程序看到的結(jié)果。當(dāng)然運(yùn)行結(jié)果取決于運(yùn)行程序的計(jì)算機(jī),但是兩者的數(shù)值比不會(huì)有太大變化。
圖 10.2: 例Vspeed中整型與Variant類型的計(jì)算速度差異
結(jié)束語
Variant 類型與傳統(tǒng)Pascal 數(shù)據(jù)類型差別很大,所以本章以短小篇幅單獨(dú)闡述了Variant類型的有關(guān)內(nèi)容。盡管Variant類型主要用于OLE 編程,但用來寫一些潦潦草草的程序倒也便利,因?yàn)椴挥每紤]數(shù)據(jù)類型,不過正如以上所述,這樣做會(huì)影響程序執(zhí)行速度。