F#命名空间

2020-08-10 10:31 更新

命名空间使你能够将一个名称附加到一组 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!")

递归命名空间

还可以将命名空间声明为 recursive,以允许所有包含的代码相互递归。 这是通过 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​也相互引用。 如果从rec MutualReferences命名空间中删除关键字,则无法在 F # 中表示。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号