類別 是代表可以具有屬性、方法和事件之物件的類型。
語法
// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
備註
類別代表 .NET 物件類型的基本描述;類別是支援 F# 中面向物件程式設計的主要類型概念。
在上述語法中, type-name 是任何有效的標識符。
type-params描述選擇性泛型型別參數。 它包含以角括弧 (< 和 >) 括住的類型參數名稱和條件約束。 如需詳細資訊,請參閱 泛型 和 條件約束。
parameter-list描述建構函式參數。 第一個存取修飾詞與類型相關;第二個與主要建構函式有關。 在這兩種情況下,預設值為 public。
您可以使用 關鍵詞來指定類別的 inherit 基類。 您必須在括弧中提供基類建構函式的自變數。
您可以使用系結來宣告類別 let 本機的欄位或函式值,而且您必須遵循系結的 let 一般規則。 區 do-bindings 段包含要於物件建構時執行的程序代碼。
member-list包含其他建構函式、實例和靜態方法宣告、介面宣告、抽象系結和屬性和事件宣告。 這些描述於 成員中。
identifier與選擇性as關鍵詞搭配使用的 ,會為實例變數或自我標識碼提供名稱,這可用於型別定義中,以參考型別的實例。 如需詳細資訊,請參閱本主題稍後的一節。
標記定義開頭和結尾的關鍵詞classend是選擇性的。
相互遞歸型別,也就是彼此參考的類型,會與關鍵詞聯結在 and 一起,就像相互遞歸函式一樣。 如需範例,請參閱「相互遞歸類型」一節。
建構函數
建構函式是建立類別類型的實例的程序代碼。 類別的建構函式在 F# 中的運作方式與其他 .NET 語言不同。 在 F# 類別中,一律會有主要建構函式,其自變數會在 parameter-list 類型名稱後面描述,而主體是由let類別宣告do開頭的 和 let rec系結所組成,以及後續系結。 主要建構函式的自變數在整個類別宣告範圍內。
您可以使用 關鍵詞新增成員來新增其他建構 new 函式,如下所示:
new(argument-list) = constructor-body
新建構函式的主體必須叫用類別宣告頂端指定的主要建構函式。
下列範例說明這個概念。 在下列程式代碼中, MyClass 有兩個建構函式,一個接受兩個自變數的主要建構函式,另一個建構函式不接受自變數。
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
let 和 do Bindings
let類別定義中的 和 do 系結會形成主要類別建構函式的主體,因此每當建立類別實例時,它們就會執行。 如果系 let 結是函式,則會編譯成成員。 如果系 let 結是未用於任何函式或成員的值,則會編譯成建構函式本機的變數。 否則,它會編譯成 類別的欄位。 後續 do 的表達式會編譯成主要建構函式,並針對每個實例執行初始化程序代碼。 因為任何其他建構函式一律會呼叫主要建構函式,因此不論呼叫哪一個建構函式, let 系結和 do 系結一律都會執行。
系結所 let 建立的欄位可以在整個類別的方法和屬性中存取;不過,即使靜態方法採用實例變數做為參數,也無法從靜態方法存取它們。 如果存在,則無法使用自我標識符來存取它們。
自我標識碼
自我標識碼是代表目前實例的名稱。 自我標識碼類似於 this C# 或 C++ 或 Me Visual Basic 中的 關鍵詞。 您可以透過兩種不同的方式定義自我標識符,視您想要讓自我標識符位於整個類別定義的範圍,或只針對個別方法而定。
若要定義整個類別的自我識別碼,請在建構函式參數清單的右括號後面使用 as 關鍵詞,並指定標識元名稱。
若要只定義一個方法的自我標識符,請在成員宣告中提供自我標識碼,就在方法名稱和句號 (.) 前面做為分隔符。
下列程式代碼範例說明建立自我標識符的兩種方式。 在第一行中 as ,關鍵詞是用來定義自我標識符。 在第五行中,識別碼 this 是用來定義自我識別碼,其範圍限制為 方法 PrintMessage。
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
與其他 .NET 語言不同,您可以視需要命名自我標識符;您不限於、、 或 this等selfMe名稱。
在基底建構函式之後,才會初始化以 as 關鍵詞宣告的自我標識碼。 因此,在基底建構函式之前或內部使用時, System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. 會在運行時間期間引發。 您可以在基底建構函式之後自由使用自我標識符,例如在系結或do系結中let。
泛型類型參數
泛型型別參數是以角括弧(< 和 >)的形式指定,格式為單引號後面接著標識符。 多個泛型類型參數會以逗號分隔。 泛型型別參數在整個宣告範圍內。 下列程式代碼範例示範如何指定泛型型別參數。
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
使用型別時,會推斷類型自變數。 在下列程序代碼中,推斷的類型是 Tuple 序列。
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
指定繼承
子 inherit 句會識別直接基類,如果有的話。 在 F# 中,只允許一個直接基類。 類別實作的介面不會被視為基類。 介面主題會討論 介面 。
您可以使用語言關鍵詞 base 做為標識符,後面接著句號 (.) 和成員的名稱,從衍生類別存取基類的方法和屬性。
如需詳細資訊,請參閱 繼承。
Members 區段
您可以在本節中定義靜態或實例方法、屬性、介面實作、抽象成員、事件宣告和其他建構函式。 讓和 執行系結不能出現在本節中。 由於成員除了類別之外,還可以新增至各種 F# 類型,因此它們會在個別主題 成員中討論。
相互遞歸類型
當您以迴圈方式定義相互參考的類型時,您可以使用 關鍵詞將類型定義 and 串在一起。 關鍵詞 and 會 type 取代第一個定義以外的所有 關鍵詞,如下所示。
open System.IO
type Folder(pathIn: string) =
let path = pathIn
let filenameArray: string array = Directory.GetFiles(path)
member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray
and File(filename: string, containingFolder: Folder) =
member this.Name = filename
member this.ContainingFolder = containingFolder
let folder1 = new Folder(".")
for file in folder1.FileArray do
printfn "%s" file.Name
輸出是目前目錄中所有檔案的清單。
使用類別、等位、記錄和結構的時機
假設有各種不同的類型可供選擇,您必須充分瞭解每種類型的設計,以針對特定情況選取適當的類型。 類別是專為在面向物件程式設計內容中使用而設計。 面向物件程式設計是針對 .NET Framework 所撰寫之應用程式中所使用的主要範例。 如果您的 F# 程式代碼必須與 .NET Framework 或其他面向物件連結庫密切合作,特別是如果您必須從 UI 連結庫等面向物件類型系統進行擴充,則類別可能很合適。
如果您未與面向物件程式代碼緊密互通,或撰寫獨立程式代碼的程序代碼,因此不受對象導向程式代碼頻繁互動的保護,您應該考慮使用類別、記錄和歧視聯集的混合。 單一且深思熟慮的差別聯集,以及適當的模式比對程序代碼,通常可以用來做為物件階層的更簡單替代方案。 如需歧視等位的詳細資訊,請參閱 歧視聯集。
記錄的優點是比類別更簡單,但是當類型的需求超過可使用其簡單性完成時,記錄就不合適。 記錄基本上是簡單的值匯總,沒有個別的建構函式可以執行自定義動作、沒有隱藏欄位,也不需要繼承或介面實作。 雖然屬性和方法等成員可以新增至記錄,使其行為更加複雜,但儲存在記錄中的欄位仍然是簡單的值匯總。 如需記錄的詳細資訊,請參閱 記錄。
結構也適用於小型數據匯總,但與類別和記錄不同,因為它們是 .NET 實值型別。 類別和記錄是 .NET 參考類型。 實值型別和參考型別的語意,在於實值型別會以傳值方式傳遞。 這表示當它們當做參數傳遞或從函式傳回時,會針對位複製位。 它們也會儲存在堆疊上,或者,如果使用它們作為字段,內嵌在父物件內,而不是儲存在堆積上自己的個別位置。 因此,當存取堆積的額外負荷發生問題時,結構適用於經常存取的數據。 如需結構的詳細資訊,請參閱 結構。