# 第二章
## 編寫Pascal代碼
進入正題前先談一下Pascal代碼編寫風格的問題。“除了遵循語法規則外,你應該怎樣來寫代碼呢?” 關于這個問題各人答案會有不同,因為各人喜歡的風格不同。總的來說,任何編碼風格的目標都是使代碼清楚、明晰,采用某種風格和格式只是一種簡略方法,用于更清楚地表達你的代碼要干什么。實現代碼清楚明晰的基本原則是保持代碼的一致性,也就是無論選用哪種風格,在整個工程中要始終保持同一風格。
### 注釋
在Pascal中,注釋括在大括號中或帶星號的圓括號中。Delphi 也認可C++ 風格的注釋,即把注釋放在雙斜線后。例如
* * * * *
~~~
{this is a comment}
(* this is another comment *)
// this is a comment up to the end of the line
~~~
* * * * *
第一種注釋方式較簡略,使用也較為普遍;第二種方式在歐洲使用較廣 ,因為歐洲的鍵盤缺少大括號;第三種方式的注釋是從C++借用來的,只在32位版本的Delphi中可用,它在給一行代碼加短注釋時非常有用。
在這本書中我用斜體表示注釋,用粗體表示關鍵詞,以此與默認的Delphi語法風格表示一致。
上述三種不同的注釋方式有益于進行嵌套注釋。例如你要注銷一段代碼,而代碼行中又包含真正的注釋行,這時采用同一種注釋方式是不對的:
~~~
{ ... code
{comment, creating problems}
... code }
~~~
正確的方法是插入第二種注釋方式:
~~~
{ ... code
//this comment is OK
... code }
~~~
注意:如果左大括號或圓括號-星號后面跟美元符號($),那么其中的內容就成了編譯指令,如 {$X+}。
實際上,編譯指令仍是注釋。例如,{$X+ This is a comment} 是合法的。這既是有效的編譯指令又是一條注釋,盡管明智的程序員很可能會注意把編譯指令和注釋分開。
### 使用大寫字母
Pascal 編譯器(不象其他語言的編譯器)不考慮字符的大小寫,因此標識符Myname、 MyName、 myname、 myName、 和MYNAME是完全相同的。總體上來說,這是Pascal的一大優點,因為在大小寫敏感的語言中,許多語法錯誤是由不正確的大寫引起的。
注意:Pascal語言的大小寫不敏感特性有一個例外:控件包中的Register 過程必須以大寫字母R開始,因為需要與C++Builder 兼容。
然而大小寫不敏感也有不便之處:第一,你必須注意大小寫不一致的標識符實際上是相同的,以避免把他們當成不同的元素使用;第二,你必須盡量保持大寫使用的一致性,以提高代碼的可讀性。
大寫使用的一致性不是編譯器強制要求的,但是保持大寫使用的一致性是值得提倡的好習慣。一個常用的方法是將每個標識符的第一個字母大寫,標識符若由幾個詞組合而成(中間不能插入空格),每個詞的第一個字母應大寫:
~~~
MyLongIdentifier
MyVeryLongAndAlmostStupidIdentifier
~~~
此外,編譯器不編譯代碼中的空格、空行和Tab鍵空格,這些元素通稱為空白,它們只用來提高代碼的可讀性,不影響編譯過程。
不同于BASIC, Pascal 語句允許分行書寫,即將一條長指令分割成兩個或更多的代碼行。允許語句分行的缺點(至少對許多BASIC程序員)是:語句結束時不能忘了加分號,更確切地說,必須記著把語句和緊接它的語句分開。語句分行唯一的限制是字符串不能跨行。
關于空格和語句分行的使用沒有既定的規則,以下是幾點經驗:
Delphi 代碼編輯器中有一條豎線叫右邊線(Right Margin),你可以把右邊線設置在60或70個字符處。如果以這條線為基準,代碼不超過這條界限,那么打印到紙上的代碼看起來會很好看。否則,打印時長語句會被隨意分行,甚至在一個詞的中間斷開。
當一個函數或過程有多個參數,通常的做法是把各參數放在不同的行上。
你可以在注釋行前留一行空白,或把長的代碼句分成較小的部分,這樣能提高代碼的可讀性。
用空格隔開函數調用的參數,表達式中的運算符也最好用空格隔開。一些程序員可能會對這些提議不以為然,但我堅持認為:空格是免費的,你不必為使用空格付費,何樂而不為呢?
優化版面
關于代碼編寫風格的最后一條建議是:盡量使用空白優化版面。這一條很容易做到,只需要在寫復合句時,以上一句為參照,下一句向右縮進兩個空格,復合句內嵌的復合句縮進四個空格,依此類推。例如:
~~~
if ... then
statement;
if ... then
begin
statement1;
statement2;
end;
if ... then
begin
if ... then
statement1;
statement2;
end;
~~~
相似的縮進格式常用于變量或數據類型聲名區,也可用于語句的續行:
~~~
type
Letters = set of Char;
var
Name: string;
begin
{ long comment and long statement, going on in the
following line and indented two spaces }
MessageDlg ('This is a message',
mtInformation, [mbOk], 0);
~~~
提出以上代碼編寫格式只是向你建個議而已,這樣代碼能更加易讀,其實代碼格式并不影響編譯結果。在本書的例子和代碼段中我始終堅持使用上述代碼風格,Delphi 中的源代碼、手冊和幫助例子均采用了相似的格式化風格。
### 突出Pascal元素
為了使Pascal 代碼更易讀寫,Delphi 編輯器中增加了Pascal 元素的色彩設置功能,也就是編輯器會用不同的顏色表示不同的Pascal 元素。缺省情況下,關鍵字以粗體表示,字符串和注釋用藍色表示(并且常常是斜體)。
用不同色彩顯示不同的Pascal 元素對保留字、注釋和字符串十分有利,因為著色后你一眼就可以看出拼錯的關鍵字、沒有正常結束的字符串及多行注釋。
使用編輯器環境選項對話框中的色彩(Color)頁,很容易就能定制各種Pascal 元素的色彩(見圖2.1)。如果獨自工作,那么你可隨意選擇喜歡的顏色。如果是與其他程序員合作,那么應該使用大家統一的標準顏色。我感覺在同一臺計算機上使用我不習慣的色彩配置確實很難受。

**圖2.1 編輯環境設置對話框**
注意:本書中我選用了一種色彩方案來顯示源代碼清單,希望能使代碼更易讀。
### 使用代碼模板
Delphi 3 中增加了用于代碼編輯的新功能“代碼模板”。由于寫Pascal 語句時,常常會重復鍵入相同的一組關鍵字,為此Borland 公司開發了名為“代碼模板”的新功能,代碼模板中存放了能與代碼縮略形式對應的完整代碼,你輸入縮略代碼,然后按Ctrl+J,完整的代碼就出現了。例如,你輸入arrayd,然后按Ctrl+J,Delphi 編輯器會把你的文本擴展為:
~~~
array [0..] of ;
~~~
由于同一種代碼結構在預定義的代碼模板中通常有多種樣式,所以模板中的縮略形式一般加有一個后綴字母,以便你選用。此外,你也可以只輸入縮略形式的頭幾個字母,如你輸ar,然后按Ctrl+J,那么,編輯器中會彈出一個菜單,菜單中列出了代碼縮略形式選項,見圖2.2所示。

**圖2.2 代碼模板選項**
代碼模板可以定制,就是你可以修改已有的模板也可以添加自己常用的代碼段。用代碼模板輸入的代碼文本中通常會出現‘|’字符,它表示輸入模板代碼后光標應跳到的位置,就是說你應該從這個光標位置開始輸入,寫完這句代碼。
### 編程語句
標識符一經定義 ,你就可以在語句及組成語句的表達式中使用它們。Pascal 提供了許多語句和表達式,首先來看看關鍵字、表達式和運算符。
### 關鍵字
關鍵字是Object Pascal 的保留標識符,在語言中有著特殊含義。保留字不能用作標識符,指令字也同樣不應該用作標識符,即使編譯器允許也最好不用。在實際中你不應該把任何關鍵字用作標識符。
表2.1是面向對象 Pascal 語言(Delphi 4)中特殊標識符的完整列表,其中包括關鍵字及保留字。
**表2.1:面向對象Pascal語言中的關鍵字及保留字**
|關鍵字 | 作用 |
| --- | --- |
|absolute | 指令 (變量)|
|abstract |指令 (方法) |
|and | 運算符 (布爾)|
|array| 類型|
|as |運算符 (RTTI)|
|asm |語句 |
|assembler | 向后兼容 (匯編) |
|at |語句 (異常處理) |
|automated |訪問類別符 (類)|
|begin | 塊標記 |
|case |語句|
|cdecl | 函數調用協定 |
|class | 類型|
|const | 聲明或指令(參數)|
|constructor| 特殊方法|
|contains |運算符 (集合) |
|default |指令 (屬性) |
|destructor | 特殊方法 |
|dispid | dispinterface界面類別符 |
|dispinterface |類型 |
|div |運算符|
|do |語句|
|downto | 語句 (for)|
|dynamic | 指令 (方法)|
|else | 語句 (if 或 case) |
|end |塊標記 |
|except |語句 (異常處理) |
|export |向后兼容 (類)|
|exports | 聲明 |
|external |指令 (函數) |
|far | 向后兼容 (類) |
|file |類型 |
|finalization| 單元結構|
|finally |語句 (異常處理)|
|for |語句|
|forward | 函數指令 |
|function |聲明 |
|goto | 語句|
|if | 語句|
|implementation| 單元結構 |
|implements | 指令 (屬性) |
|in |運算符 (集合) - 工程結構 |
|index |指令 (dipinterface界面) |
|inherited |語句|
|initialization |單元結構|
|inline |向后兼容 (見 asm)|
|interface |類型|
|is |運算符 (RTTI) |
|label | 聲明|
|library | 程序結構|
|message |指令 (方法) |
|mod | 運算符 (數學)|
|name | 指令 (函數)|
|near | 向后兼容 (類)|
|nil | 數值 |
|nodefault | 指令 (屬性) |
|not |運算符 (布爾) |
|object |向后兼容 (類) |
|of | 語句 (case) |
|on |語句 (異常處理)|
|or |運算符 (布爾)|
|out |指令 (參數)|
|overload| 函數指令 |
|override| 函數指令 |
|package | 程序結構 (控件包)|
|packed | 指令 (記錄) |
|pascal | 函數調用協定 |
|private | 訪問類別符 (class) |
|procedure| 聲明 |
|program |程序結構 |
|property |聲明 |
|protected |訪問類別符 (類)|
|public |訪問類別符 (類)|
|published |訪問類別符 (類)|
|raise |語句 (異常處理) |
|read |屬性類別符 |
|readonly |dispatch界面類別符 |
|record | 類型 |
|register |函數調用協定 |
|reintroduce |函數指令 |
|repeat | 語句|
|requires |程序結構 (控件包) |
|resident |指令 (函數) |
|resourcestring| 類型|
|safecall | 函數調用協定|
|set |類型|
|shl | 運算符 (數學)|
|shr | 運算符 (數學)|
|stdcall| 函數調用協定 |
|stored | 指令 (屬性) |
|string |類型 |
|then | 語句 (if) |
|threadvar |聲明 |
|to |語句 (for) |
|try |語句 (異常處理) |
|type |聲明|
|unit |單元結構|
|until |語句 |
|uses |單元結構 |
|var |聲明 |
|virtual |指令 (方法) |
|while |語句 |
|with | 語句 |
|write |屬性類別符|
|writeonly |dispatch 界面類別符 |
|xor |運算符 (布爾) |
### 表達式和運算符
建立表達式沒有通用的方法,因為要取決于所用的運算符,Pascal包括有邏輯運算符、算術運算符、布爾運算符、關系運算符和集合運算符等等。表達式可用于確定賦給一個變量的值、計算函數或過程的參數、或者判斷一個條件,表達式也可以包含函數調用。表達式是對一個標識符的值而不是標識符本身進行運算。
所有編程語言中的表達式都是常量、變量、數值、運算符和函數值的合法組合。表達式可以傳遞給過程或函數的值參,但不能傳遞給過程或函數中的引用參數。
### 運算符及其優先級
如果你以前寫過程序,那么你已經知道表達式是什么了。這里我專門講一下Pascal 運算符的特殊部分:運算符的優先級。表2.2中按優先級分組列出了Pascal語言的運算符。
> 與大多數編程語言相反,Pascal語言中and和or運算符的優先級比關系運算符高。因此,如果你的代碼為a < b and c < d,編譯器首先會編譯and運算符,由此導致編譯出錯。為此你應該把每個 < 表達式用小括號括起來: (a < b) and (c < d)。
同一種運算符用于不同數據類型時它的作用不同。例如,運算符 + 可以計算兩個數字的和、連接兩個字符串、求兩個集合的并集、甚至給PChar 指針加一個偏移量。然而,你不能象在C語言中那樣將兩個字符相加。
另一個特殊的運算符是 div。在Pascal 中,你能用 / 計算兩個數字(實數或整數)的商,而且你總能得到一個實型結果。如果計算兩個整數的商并想要一個整型結果,那么就需要用 div 運算符。
**表 2.2: Pascal語言中的運算符及其優先級**
**單目運算符 (最高優先級**)|
|@ |取變量或函數的地址(返回一個指針) |
| --|-- |
|not |邏輯取反或按位取反 |
**乘除及按位運算符**
|*| 相乘或集合交集 |
| --|-- |
|/| 浮點相除 |
|div |整數相除 |
|mod |取模 (整數相除的余數) |
|as| 程序運行階段類型轉換 (RTTI運算符) |
|and |邏輯或按位求和 |
|shl |按位左移 |
|shr |按位右移 |
加減運算符
|+|相加、集合并集、字符串連接或指針增加一個偏移量|
| --|-- |
|- |相減、集合差集或指針減少一個偏移量 |
|or |邏輯或按位或運算|
|xor| 邏輯或按位異或運算|
關系及比較運算符(最低優先級)
|= |判斷是否相等|
| --|-- |
|<>| 判斷是否不相等 |
|<| 判斷是否小于 |
|>| 判斷是否大于 |
|<= |判斷是否小于或等于,或是否是一個集合的子集|
|>= |判斷是否大于或等于,或是否是一個集合的父集|
|in| 判斷是否是集合成員 |
|is| 判斷對象是否類型兼容 (又一個RTTI運算符) |
### 集合運算符
集合運算符包括并(+)、差(-)、交(*)、成員檢測(in),及一些關系運算符。要把一個元素添加到集合中,你可以采用集合并運算。下面是一個選擇字體的Delphi 例子:
~~~
Style := Style + [fsBold];
Style := Style + [fsBold, fsItalic] - [fsUnderline];
~~~
另一種方法是利用標準過程Include 和Exclude,它們效率更高(但不能用于控件的集合類型屬性,因為只能操縱一個元素):
~~~
Include (Style, fsBold);
~~~
* * * * *
### 結束語
從上面內容我們已經了解了Pascal 程序的基本布局,下面開始探究它的細節。先從預定義和自定義數據類型開始,然后是利用關鍵詞組織編程語句。