共用方式為


模組

在 F# 的內容中,「模組」是 F# 程式碼群組,例如 F# 程式中的值、類型和函式值。 程式碼分組成不同模組有助於將相關程式碼整理到同一處,以及避免程式中發生名稱衝突。

語法

// Top-level module declaration.
module [accessibility-modifier] [qualified-namespace.]module-name
declarations
// Local module declaration.
module [accessibility-modifier] module-name =
    declarations

備註

F# 模組是 F# 程式碼建構的群組,例如 do 繫結中的類型、值、函式值和程式碼。 它會實作為只有靜態成員的 Common Language Runtime (CLR) 類別。 模組宣告有兩種類型 (最上層模組宣告和本機模組宣告),視整個檔案是否包含在模組中而定。 最上層模組宣告會在模組中包含整個檔案。 最上層模組宣告只能顯示為檔案中的第一個宣告。

在最上層模組宣告的語法中,選擇性的 qualified-namespace 是包含模組的巢狀命名空間名稱序列。 不需要在之前先宣告限定命名空間。

您不需要縮排最上層模組中的宣告。 您必須縮排本機模組中的所有宣告。 在本機模組宣告中,只有在該模組宣告下縮排的宣告是模組的一部分。

如果程式碼檔案不是以最上層模組宣告或命名空間宣告開頭,則檔案的整個內容 (包括任何本機模組) 會成為隱含建立之最上層模組的一部分 (該模組的名稱與檔案相同,但不含副檔名,且第一個字母會轉換成大寫)。 例如,請考慮下列檔案。

// In the file program.fs.
let x = 40

此檔案會如以下撰寫方式進行編譯:

module Program
let x = 40

如果您在檔案中有多個模組,則必須針對每個模組使用本機模組宣告。 如果宣告封入命名空間,則這些模組會是封入命名空間的一部分。 如果未宣告封入命名空間,則這些模組會成為隱含建立之最上層模組的一部分。 下列程式碼範例顯示包含多個模組的程式碼檔案。 編譯器會隱含建立名為 Multiplemodules 的最上層模組,並將 MyModule1MyModule2 巢狀於該最上層模組。

// In the file multiplemodules.fs.
// MyModule1
module MyModule1 =
    // Indent all program elements within modules that are declared with an equal sign.
    let module1Value = 100

    let module1Function x =
        x + 10

// MyModule2
module MyModule2 =

    let module2Value = 121

    // Use a qualified name to access the function.
    // from MyModule1.
    let module2Function x =
        x * (MyModule1.module1Function module2Value)

如果您在專案或單一編譯中有多個檔案,或正在建置程式庫,則必須在檔案頂端包含命名空間宣告或模組宣告。 F# 編譯器只會在專案或編譯命令列中僅有一個檔案,且您正在建立應用程式時,隱含決定模組名稱。

accessibility-modifier 可以是下列其中一項:publicprivateinternal。 如需詳細資訊,請參閱存取控制。 預設值是公用。

參考模組中的程式碼

當您參考另一個模組中的函式、類型和值時,必須使用限定名稱或開啟模組。 如果您使用限定名稱,則必須為想要的程式元素指定命名空間、模組和識別碼。 請以點 (.) 分隔限定路徑的每個部分,如下所示。

Namespace1.Namespace2.ModuleName.Identifier

您可以開啟模組或是一或多個命名空間來簡化程式碼。 如需開啟命名空間和模組的詳細資訊,請參閱匯入宣告:open關鍵字

下列程式碼範例顯示最上層模組,其中包含截至檔案結尾的所有程式碼。

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

若要從相同專案中的另一個檔案使用此程式碼,您可以使用限定名稱,或在使用函式之前開啟模組,如下列範例所示。

// Fully qualify the function name.
let result1 = Arithmetic.add 5 9
// Open the module.
open Arithmetic
let result2 = add 5 9

巢狀模組

模組可以是巢狀的。 內部模組必須縮排到和外部模組宣告一樣遠,以指出這是內部模組,而不是新的模組。 例如,比較下列兩個範例。 在下列程式碼中,模組 Z 是內部模組。

module Y =
    let x = 1

    module Z =
        let z = 5

但在下列程式碼中,模組 Z 與模組 Y 同層級。

module Y =
    let x = 1

module Z =
    let z = 5

在下列程式碼中,模組 Z 也是同層級模組,因為沒有縮排到和模組 Y 中的其他宣告一樣遠。

module Y =
        let x = 1

    module Z =
        let z = 5

最後,如果外部模組沒有宣告,且緊接著另一個模組宣告,則新的模組宣告會假設為內部模組,但編譯器會在第二個模組定義未縮排到比第一個還遠時警告您。

// This code produces a warning, but treats Z as a inner module.
module Y =
module Z =
    let z = 5

若要消除警告,請縮排內部模組。

module Y =
    module Z =
        let z = 5

如果您希望檔案中的所有程式碼都位於單一外部模組內,並需要內部模組,則外部模組不需要等號,且要放在外部模組的宣告 (包括任何內部模組宣告)都不需要縮排。 內部模組宣告內的宣告必須縮排。 下列程式碼顯示此案例。

// The top-level module declaration can be omitted if the file is named
// TopLevel.fs or topLevel.fs, and the file is the only file in an
// application.
module TopLevel

let topLevelX = 5

module Inner1 =
    let inner1X = 1
module Inner2 =
    let inner2X = 5

遞迴模組

F# 4.1 引進了模組的概念,允許所有內含程式碼相互遞迴。 這是透過 module rec 來完成。 使用 module rec 可以減輕無法撰寫在類型和模組之間相互參考之程式碼的一些困擾。 以下是一個範例:

module rec RecursiveModule =
    type Orientation = Up | Down
    type PeelState = Peeled | Unpeeled

    // This exception depends on the type below.
    exception DontSqueezeTheBananaException of Banana

    type Banana(orientation : Orientation) =
        member val IsPeeled = false with get, set
        member val Orientation = orientation with get, set
        member val Sides: PeelState list = [ Unpeeled; Unpeeled; Unpeeled; Unpeeled] with get, set

        member self.Peel() = BananaHelpers.peel self // Note the dependency on the BananaHelpers module.
        member self.SqueezeJuiceOut() = raise (DontSqueezeTheBananaException self) // This member depends on the exception above.

    module BananaHelpers =
        let peel (b: Banana) =
            let flip (banana: Banana) =
                match banana.Orientation with
                | Up ->
                    banana.Orientation <- Down
                    banana
                | Down -> banana

            let peelSides (banana: Banana) =
                banana.Sides
                |> List.map (function
                             | Unpeeled -> Peeled
                             | Peeled -> Peeled)

            match b.Orientation with
            | Up ->   b |> flip |> peelSides
            | Down -> b |> peelSides

請注意,例外狀況 DontSqueezeTheBananaException 和類別 Banana 會彼此參考。 此外,模組 BananaHelpers 和類別 Banana 也會彼此參考。 如果您已從 RecursiveModule 模組中移除 rec 關鍵字,就無法在 F# 中表達。

您也可以使用 F# 4.1 在命名空間中執行這項功能。

另請參閱