Object Pascal 的 with...do 語法的確很好用,可以少打好多字,正所謂水能載舟,亦能覆舟,好用的東西通常有些特殊事項要遵守,才能用得恰到好處。看看以下這個極端的例子:
procedure TForm1.Button1Click(Sender: TObject); begin with Label1, Button1, ListBox1, ListBox2, Memo1 do begin Tag := 2; Caption := 'Who'; Color := clRed; Items[0] := 'aa'; end; end;
先不管在語意上的混淆不清,如果你不完全了解 with...do 的行為,你能單單從程式碼預測其執行的結果嗎?
當然很少人寫程式會這麼極端,但是只要 with 裡面有兩個物件就夠你受的了,我的意思是,當你寫完程式三個月後再回來維護這些程式時,還要再花腦筋回憶當初撰寫的邏輯,並且試著找出到底是哪一行出了問題,你也許會像我一樣曾經納悶,明明屬性有設定了,怎麼都沒作用....如果是別人接手維護你的程式其痛苦程度可想而知了。此外,巢狀的
with...do 也要避免,原因也是一樣---程式出錯時很難抓蟲。
再看看這個例子,你能看出 TForm1.Button1Click 的執行結果嗎?
type TEmployee = record EmpID: string; EmpName: string; end; {... TForm1 的類別定義省略 } var Form1: TForm1; EmpArray: array [0..9] of TEmployee; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); var i: integer; begin for i := Low(EmpArray) to High(EmpArray) do begin EmpArray[i].EmpID := IntToStr(i); EmpArray[i].EmpName := 'Emp' + IntToStr(i); end; end; procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin Memo1.Clear; i := 0; with EmpArray[i] do begin while i < 10 do begin Memo1.Lines.Add(EmpName); Inc(i); end; end; end; end.
原本撰寫 Button1Click 的程式碼的人可能是想要把 EmpArray 裡面每個員工的姓名輸出到一個 Memo 元件,可是程式執行的結果是所有輸出的員工姓名都一樣,都是陣列的第 0 個元素的員工姓名。我曾經除錯一個別人寫的程式,就是這麼樣寫法,讓人弄不清楚他寫這樣的原意到底是要固定取第 0 個元素,還是要用迴圈取出所有陣列元素,卻因為不注意而寫成了這樣?你可以看得出來,這樣子使用 with...do 所造成的語意混淆不清有多麼危險。
那什麼時候該用,該怎麼用才算恰到好處,Ok,這有點兒主觀,我使用 with 的原則是:盡量少用,有用的話也是只對一個物件,絕對不要同時針對兩個物件,也不要使用巢狀 with 。在大部分的情況下,你都可以透過將物件指定給另一個指標,再利用該指標來操作的方式來取代 with 。像這樣:
var t: TTable; p: ^TEmployee; begin p := EmpArray[0]; p^.EmpNAme := 'Michael'; .... t := EmployeeDataModule.EmployeeTable; t.Open; .... end;
只要多打一些些字,就可能減少不必要的臭蟲及日後維護的成本,實在是很划算的交易,不是嗎?
蔡煥麟 Aug-30-2000