我學Delphi心得及筆記—-使用者自訂資料類型(第三講)

來源:互聯網
上載者:User

Pascal 語言的一個重要特徵是它能自訂資料類型。通過各種類型構造器,你可以定義自己的資料類型,如子界類型、數群組類型、記錄類型、枚舉類型、指標類型和集合類型。最重要的使用者定義資料類型是類(class),類是Object Pascal的物件導向擴充部分,本書不討論這部分。

你可能會認為其它程式設計語言也有諸如此類的類型構造器,確實如此,但是Pascal 是第一個完美實現這一理論的語言。至今仍然沒有語言有能力定義那麼多的資料類型。

命名及不命名的類型

為了後續使用或直接用於變數,需要給自訂類型命名。如果自訂一個命名的類型,你必須將代碼放在特定的type區,如下所示:

type  // subrange definition  Uppercase = 'A'..'Z';  // array definition  Temperatures = array [1..24] of Integer;  // record definition  Date = record    Month: Byte;    Day: Byte;    Year: Integer;  end;  // enumerated type definition  Colors = (Red, Yellow, Green, Cyan, Blue, Violet);  // set definition  Letters = set of Char;

你也可使用類型定義構造器直接定義一個變數,此時無需顯式命名,如下面的代碼:

var  DecemberTemperature: array [1..31] of Byte;  ColorCode: array [Red..Violet] of Word;  Palette: set ofColors;

注意:一般來說,你應該避免使用上述不命名類型,因為你不能把它們作為參數傳給常式,也不能用於聲名同一類型的其他變數。實際上,Pascal的類型相容規則是基於類型名的,而不是基於實際的類型定義。兩個類型相同的變數仍有可能是不相容的,除非他們的類型有完全相同的名字。對於不命名類型,需要編譯器給它分配一個內部名字,因此對於資料結構複雜的變數,要習慣於定義命名資料類型,你一定不會為此白費工夫的。

但是上述自訂類型有什麼意義呢?如果你不太熟悉Pascal類型構造器,通過下面內容你會瞭解它,此外下面還談到了同類構造器在不同語言中的差異,因此如果你已熟知上面例舉的類型定義,不妨往下讀,你會對其中內容感興趣的。最後,我將示範一些Delphi例子,並介紹一些能動態訪問類型資訊的工具。

子界類型
子界類型定義了某種類型的取值範圍(因此定名subrange)。你可定義整數類型的子界類型,如取值從1到10或從100到1000,或者定義字元 類型的子界類型,如下所示:
type  Ten = 1..10;  OverHundred = 100..1000;  Uppercase = 'A'..'Z';
定義子界類型時,你不需要指定基類的名字,而只需提供該類型的兩個常數。所用基類必須是有序類型,定義結果將是另一種有序類型。
如定義一個子界變數,那麼賦給該變數的值必須是子界定義範圍內的值。下面代碼是正確的:
var  UppLetter: UpperCase;begin  UppLetter := 'F';
以下代碼則是不正確的:
var  UppLetter: UpperCase;begin  UppLetter := 'e'; // compile-time error
以上代碼將導致一個編譯錯誤:“Constant expression violates subrange bounds”。
如果代之以下面代碼:
var  UppLetter: Uppercase;  Letter: Char;begin  Letter :='e';  UppLetter := Letter;
Delphi 編譯會通過,但在運行時,如果你開啟了範圍檢查編譯選項(在工程選項對話方塊的編譯器頁設定),你將得到 Range check error (範圍檢測錯誤)資訊。
注意:建議你在開發程式時開啟上述編譯選項,以使程式更健壯並易於調試。這樣即使遇上錯誤,你也會得到一個明確的資訊而不是難以琢磨的行為。最終完成程式時你可以去掉這個選項,使程式運行得快一些,不過影響很小。因此我建議你開啟所有運行時的檢測選項,如溢出檢查和堆棧檢查,甚至提交程式時仍然保留它們。
枚舉類型 
 枚舉類型又是一種自訂有序類型。在枚舉類型中,你列出所有該類型可能取的值,而不是指定現有類型的範圍。換句話說,枚舉類型是個可取值的序列。見下例:
type  Colors = (Red, Yellow, Green, Cyan, Blue, Violet);  Suit = (Club, Diamond, Heart, Spade);
序列中每個值都對應一個序號,序號從0開始計數。使用Ord 函數,即可得到一個枚舉類型值的序號。例如,Ord (Diamond) 傳回值1。
注意:枚舉類型有多種內部標記法。預設時,Delphi 用8位標記法;如果有多於256個不同的值,則用16位標記法。還有一種32位標記法,需要與C、C++庫相容時會用到。使用$Z 編譯指令可改變預設設定,請求更多位的標記法。
集合類型

集合類型表示一組值,該組值由集合所依據的有序類型定義。定義集合的常用有序類型不多,一般為枚舉類型或子界類型。如果子界類型取值為1..3,那麼基於它的集合類型值可以是1、或2、或3、或1和2、或1和3、或2和3、或取所有3個數、或一個數也沒有。

一個變數通常包含該類型對應的一個值,而集合類型可以不包含值、包含一個值、兩個值、三個值,或更多,它甚至可以包含定義範圍內所有的值。下面定義一個集合:

type  Letters = set of Uppercase;

現在我可以用上面類型來定義變數,並把原始類型的值賦給變數。為了在集合中表示一組值,需要用逗號將值隔開,最後用方括弧結尾。下例顯示了多值、單值和空值的變數賦值:

var  Letters1, Letters2, Letters3: Letters;begin  Letters1 := ['A', 'B', 'C'];  Letters2 := ['K'];  Letters3 := [];

在Delphi中,集合一般用於表示有多種選擇的標記。例如下面兩行代碼(摘自Delphi庫)聲明了一個枚舉類型,其中列出了視窗條上可選的表徵圖,並聲明了相應的集合類型:

type  TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);  TBorderIcons = set of TBorderIcon;
數群組類型

數群組類型定義了一組指定類型的元素序列,在方括弧中填入下標值就可訪問數組中的元素。定義數組時,方括弧也用來指定可能的下標值。例如,下面的代碼中定義了一個有24個整數的數組:

type  DayTemperatures = array [1..24] of Integer;

在數組定義時,你需要在方括弧中填入一個子界類型的值,或者用兩個有序類型的常量定義一個新的子界類型,子界類型指定了數組的有效索引。由於子界類型指定了數組下標值的上界和下界,那麼下標就不必象C、C++、JAVA和其他語言那樣必須從零開始。

由於數組下標基於子界類型,因此Delphi 能夠對它們進行範圍檢查。不合法的常量子界類型將導致一個編譯時間錯誤;如果選上編譯器範圍檢查選項,那麼超出範圍的下標值將導致一個已耗用時間錯誤。

使用上述數組定義方法,定義一個DayTemperatures 類型的變數如下:

type  DayTemperatures = array [1..24] of Integer;var    DayTemp1: DayTemperatures;  procedure AssignTemp;  begin    DayTemp1 [1] := 54;  DayTemp1 [2] := 52;  ...  DayTemp1 [24] := 66;  DayTemp1 [25] := 67; // compile-time error

數組可以是多維的,如下例:

type  MonthTemps = array [1..24, 1..31] of Integer;  YearTemps = array [1..24, 1..31, Jan..Dec] of Integer;

這兩個數組建立在相同的核心類型上,因此你可用前面定義的資料類型聲明它們,如下面代碼所示:

type  MonthTemps = array [1..31] of DayTemperatures;  YearTemps = array [Jan..Dec] of MonthTemps;

上例的聲明把索引的次序前後調換了一下,但仍允許變數之間整塊賦值。例如:把一月份的溫度值賦給二月份:

var  ThisYear: YearTemps;begin  ...  ThisYear[Feb] := ThisYear[Jan];

你也能定義下標從零開始的數組,不過這似乎不太合邏輯,因為你需要用下標2來訪問數組第三項。然而,Windows一直沿用了從零開始的數組(因為它是基於C語言的),並且Delphi 控制項陳列庫也在往這方向靠攏。

使用數組時,你總要用標準函數LowHigh來檢測它的邊界,LowHigh返回下標的下界和上界。強烈建議使用LowHigh運算元組,特別是在迴圈中,因為這樣能使代碼與數組範圍無關,如果你改變數組下標的範圍聲明,LowHigh代碼不會受影響;否則,如果代碼中有一個數組下標迴圈體,那麼當數組大小改變時你就不得不更新迴圈體的代碼。Low High將使你的代碼更易於維護、更穩定。

注意:順便提一下,使用LowHigh不會增加系統運行額外開銷。因為在編譯時間,他們已被轉換成常數運算式,而不是實際函數調用。其他簡單的系統函數也是這樣。

記錄類型

記錄類型用於定義不同類型資料項目的固定集合。記錄中每個元素,或者說域,有它自己的類型。記錄類型定義中列出了所有域,每個域對應一個網域名稱,通過網域名稱可以訪問它。

下面簡單列舉了記錄類型的定義、類型變數的聲明以及這類變數的使用:

type  Date = record    Year: Integer;    Month: Byte;    Day: Byte;  end;  var  BirthDay: Date;  begin  BirthDay.Year := 1997;  BirthDay.Month := 2;  BirthDay.Day := 14;

類和對象可以看作是記錄類型的擴充。Delphi 庫趨向於用類替代記錄類型,不過Windows API中定義了許多記錄類型。

記錄類型中允許包含variant 域,它表示多個域能公用同一記憶體區,而且域可以是不同類型(這相應於C語言中的聯合union)。換句話說,你可以通過variant 域或說是一組域訪問記錄中同一個記憶體位置,但是各個值仍需區別對待。variant類型主要用來存貯相似但又不同的資料,進行與類型映射(typecasting)相似的類型轉換(自從typecasting 引入Pascal,已很少用到這種方法了)。雖然Delphi在一些特殊情況下還在用variant 記錄類型,但是現在已經被物件導向技術或其他現代技術代替了。

variant 記錄類型的應用不符合型別安全原則,因此不提倡在編程中使用,初學者更是如此。實際上,專家級的編程人員確實需要用到variant 記錄類型,Delphi 庫的核心部分就用到了這一類型。不管怎樣,除非你是個Delphi 專家,否則你應避免使用variant記錄類型。

指標

指標是存放指定類型(或未定義類型)變數記憶體位址的變數,因此指標間接引用一個值。定義指標不需用特定的關鍵字,而用一個特殊字元,這個特殊字元是脫字元號(^),見下例:

type  PointerToInt = ^Integer;

一旦你定義了指標變數,你就可以用@ 符號把另一個相同類型變數的地址賦給它。見下例:

var  P: ^Integer;  X: Integer;begin  P := @X;  // change the value in two different ways  X := 10;  P^ := 20;  

如果定義了一個指標P,那麼P表示指標所指向的記憶體位址,而P^表示記憶體所儲存的實際內容。因此,在上面的代碼中, P^ 與X相等。

除了表示已指派記憶體的地址外,指標還能通過New 常式在堆中動態分配記憶體,不過當你不需要這個指標時,你也必須調用Dispose 常式釋放你動態分配的記憶體。

Delphi 還定義了一個Pointer 資料類型,它表示無類型的指標(就象C語言中的void* )。如果你使用無類型指標,你應該用GetMem 常式,而不是New常式,因為GetMem 常式能用於記憶體配置大小不確定的情況。

實際上,Delphi 中必須使用指標的情況很少,這是Delphi開發環境一個誘人的優點。雖然如此,若要進行進階編程和完全理解Delphi 物件模型,理解指標是很重要的,因為Delphi 物件模型在幕後使用了指標。

注意:雖然在Delphi中不常使用指標,但是你經常會用一個極為相似的結構--引用(references)。每個對象執行個體實際上是一個隱含的指標,或說是對其實際資料的引用,利用引用,你能象用其他資料類型一樣使用物件變數。

檔案類型

另一個Pascal特定的類型構造器是檔案類型(file)。檔案類型代表物理磁碟檔案,無疑是Pascal語言的一個特殊類型。按下面的方式,你可以定義一個新的資料類型:

type  IntFile = file of Integer;

然後,你就能開啟一個與這個結構相應的物理檔案、向檔案中寫入整數、或者從檔案中讀取當前的值。

Pascal 檔案類型的使用很直觀,而且Delphi 中也定義了一些控制項用於檔案儲存和裝載,以及對資料流和資料庫的支援。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.