命名空间 (F#)
该命名空间通过使你能够将名称附加一组 F# 程序元素,实现将代码整理到相关的功能区域。 命名空间通常是 F# 文件中的顶级元素。
语法
namespace [rec] [parent-namespaces.]identifier
备注
如果要将代码放在命名空间中,文件中的第一个声明必须声明该命名空间。 整个文件的内容将成为命名空间的一部分,前提是文件中不存在其他命名空间声明。 如果是这种情况,则直到下一个命名空间声明之前的所有代码都被认为在第一个命名空间内。
命名空间不能直接包含值和函数。 相反,值和函数必须包含在模块中,且模块包含在命名空间中。 命名空间可以包含类型和模块。
XML 文档注释可以在命名空间之上声明,但会被忽略。 编译器指令也可以在命名空间之上声明。
命名空间可以用 namespace 关键字显式声明,也可以在声明模块时隐式声明。 若要显式声明命名空间,请使用 namespace 关键字,后跟命名空间名称。 以下示例显示了声明命名空间 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
例如,你还可以使用 global 来引用顶级 .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 (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
也相互引用。 如果从 MutualReferences
命名空间中移除 rec
关键字,这将无法在 F# 中表达。
此功能也适用于顶级模块。