命名空间 (F#)

通过命名空间,可以通过将名称附加到 F# 程序元素分组,将代码组织到相关功能区域。 命名空间通常是 F# 文件中的顶级元素。

语法

namespace [rec] [parent-namespaces.]identifier

注解

如果要在命名空间中放置代码,文件中的第一个声明必须声明命名空间。 然后,整个文件的内容将成为命名空间的一部分,前提是文件中没有其他命名空间声明进一步存在。 如果是这种情况,则在下一个命名空间声明之前,所有代码都被视为在第一个命名空间内。

命名空间不能直接包含值和函数。 相反,值和函数必须包含在模块中,模块包含在命名空间中。 命名空间可以包含类型和模块。

XML 文档注释可以声明在命名空间上方,但将被忽略。 编译器指令也可以声明在命名空间上方。

可以使用命名空间关键字显式声明命名空间,也可以在声明模块时隐式声明命名空间。 若要显式声明命名空间,请使用命名空间关键字后跟命名空间名称。 下面的示例演示了一个代码文件,该文件声明了一个 Widgets 包含该命名空间的类型和模块的命名空间。

namespace Widgets

type MyWidget1 =
    member this.WidgetName = "Widget1"

module WidgetsModule =
    let widgetName = "Widget2"

如果文件的整个内容都位于一个模块中,则还可以使用 module 关键字隐式声明命名空间,并在完全限定的模块名称中提供新的命名空间名称。 下面的示例演示一个代码文件,该文件声明 Widgets 命名空间和一个模块 WidgetsModule,其中包含一个函数。

module Widgets.WidgetModule

let widgetFunction x y =
   printfn "%A %A" x y

以下代码等效于前面的代码,但模块是本地模块声明。 在这种情况下,命名空间必须在其自己的行上显示。

namespace Widgets

module WidgetModule =

    let widgetFunction x y =
        printfn "%A %A" x y

如果在一个或多个命名空间中的同一文件中需要多个模块,则必须使用本地模块声明。 使用本地模块声明时,不能在模块声明中使用限定的命名空间。 以下代码显示了一个具有命名空间声明和两个本地模块声明的文件。 在这种情况下,模块直接包含在命名空间中;没有与文件同名的隐式创建的模块。 文件中的任何其他代码(如 do 绑定)位于命名空间中,但不在内部模块中,因此需要使用模块名称限定模块成员 widgetFunction

namespace Widgets

module WidgetModule1 =
   let widgetFunction x y =
      printfn "Module1 %A %A" x y
module WidgetModule2 =
   let widgetFunction x y =
      printfn "Module2 %A %A" x y

module useWidgets =

  do
     WidgetModule1.widgetFunction 10 20
     WidgetModule2.widgetFunction 5 6

此示例的输出如下所示。

Module1 10 20
Module2 5 6

有关详细信息,请参阅 模块

嵌套命名空间

创建嵌套命名空间时,必须完全限定它。 否则,请创建新的顶级命名空间。 命名空间声明中忽略缩进。

以下示例演示如何声明嵌套命名空间。

namespace Outer

    // Full name: Outer.MyClass
    type MyClass() =
       member this.X(x) = x + 1

// Fully qualify any nested namespaces.
namespace Outer.Inner

    // Full name: Outer.Inner.MyClass
    type MyClass() =
       member this.Prop1 = "X"

文件和程序集中的命名空间

命名空间可以跨单个项目或编译中的多个文件。 术语 命名空间片段 描述包含在一个文件中的命名空间的一部分。 命名空间还可以跨越多个程序集。 例如, System 命名空间包括整个 .NET Framework,它跨越多个程序集,并且包含许多嵌套命名空间。

全局命名空间

使用预定义的命名空间 global 将名称放入 .NET 顶级命名空间中。

namespace global

type SomeType() =
    member this.SomeMember = 0

还可以使用全局来引用顶级 .NET 命名空间,例如,解决与其他命名空间的名称冲突。

global.System.Console.WriteLine("Hello World!")

递归命名空间

命名空间也可以声明为递归,以允许所有包含的代码相互递归。 这是通过 namespace rec. namespace rec使用可以减轻在类型与模块之间无法编写相互引用代码的一些痛苦。 下面是一个示例:

namespace rec MutualReferences

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 (banana: Banana) =
        let flip (banana: Banana) =
            match banana.Orientation with
            | Up ->
                banana.Orientation <- Down
                banana
            | Down -> banana

        // Update the peel state for all sides of the banana.
        let peelSides (banana: Banana) =
            banana.Sides
            |> List.map (function
                         | Unpeeled -> Peeled
                         | Peeled -> Peeled)

        // Apply the flipping and peeling logic based on the orientation.
        match banana.Orientation with
        | Up ->   banana |> flip |> peelSides
        | Down -> banana |> peelSides

请注意,异常 DontSqueezeTheBananaException 和类 Banana 都相互引用。 此外,模块 BananaHelpers 和类 Banana 也相互引用。 如果从命名空间中删除了 rec 关键字,则无法在 F# 中 MutualReferences 表达。

此功能也适用于顶级 模块

另请参阅