Namespaces (F#)

Mit einem Namespace können Sie Code in Bereichen verwandter Funktionalität organisieren, indem Sie einen Namen an eine Gruppierung von F#-Programmelementen anfügen. Namespaces sind in der Regel Elemente der obersten Ebene in F#-Dateien.

Syntax

namespace [rec] [parent-namespaces.]identifier

Bemerkungen

Wenn Sie Code in einen Namespace einfügen möchten, muss die erste Deklaration in der Datei den Namespace deklarieren. Der Inhalt der gesamten Datei wird dann Teil des Namespace, sofern keine andere Namespacedeklaration in der Datei vorhanden ist. In diesem Fall wird davon ausgegangen, dass der gesamte Code bis zur nächsten Namespacedeklaration im ersten Namespace enthalten ist.

Namespaces können nicht direkt Werte und Funktionen enthalten. Stattdessen müssen Werte und Funktionen in Modulen enthalten sein, und Module sind in Namespaces enthalten. Namespaces können Typen und Module enthalten.

XML-Dokumentationskommentare können über einem Namespace deklariert werden, werden aber ignoriert. Compileranweisungen können auch über einem Namespace deklariert werden.

Namespaces können explizit mit dem namespace-Schlüsselwort oder implizit beim Deklarieren eines Moduls deklariert werden. Verwenden Sie zum expliziten Deklarieren eines Namespace das namespace-Schlüsselwort gefolgt vom Namespacenamen. Das folgende Beispiel zeigt eine Codedatei, die den Namespace Widgets mit einem Typ und einem Modul in diesem Namespace deklariert.

namespace Widgets

type MyWidget1 =
    member this.WidgetName = "Widget1"

module WidgetsModule =
    let widgetName = "Widget2"

Wenn sich der gesamte Inhalt der Datei in einem Modul befindet, können Sie Namespaces auch implizit deklarieren, indem Sie das Schlüsselwort module verwenden und den neuen Namespacenamen im vollqualifizierten Modulnamen angeben. Das folgende Beispiel zeigt eine Codedatei, die den Namespace Widgets und das Modul WidgetsModule deklariert, das eine Funktion enthält.

module Widgets.WidgetModule

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

Der folgende Code entspricht dem vorherigen Code, aber das Modul ist eine lokale Moduldeklaration. In diesem Fall muss der Namespace in einer eigenen Zeile angegeben werden.

namespace Widgets

module WidgetModule =

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

Wenn mehrere Module in derselben Datei in einem oder mehreren Namespaces erforderlich sind, müssen Sie lokale Moduldeklarationen verwenden. Wenn Sie lokale Moduldeklarationen verwenden, können Sie den qualifizierten Namespace nicht in den Moduldeklarationen verwenden. Der folgende Code zeigt eine Datei mit einer Namespacedeklaration und zwei lokalen Moduldeklarationen. In diesem Fall sind die Module direkt im Namespace enthalten. Es gibt kein implizit erstelltes Modul, das denselben Namen wie die Datei hat. Jeder andere Code in der Datei, z. B. eine do-Bindung, befindet sich im Namespace, aber nicht in den inneren Modulen. Daher müssen Sie den Modulmember widgetFunction mithilfe des Modulnamens qualifizieren.

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

Die Ausgabe dieses Beispiels sieht folgendermaßen aus:

Module1 10 20
Module2 5 6

Weitere Informationen finden Sie unter Module.

Geschachtelte Namespaces

Wenn Sie einen geschachtelten Namespace erstellen, müssen Sie ihn vollständig qualifizieren. Andernfalls erstellen Sie einen neuen Namespace der obersten Ebene. Der Einzug wird in Namespacedeklarationen ignoriert.

Im folgenden Beispiel wird das Deklarieren eines geschachtelten Namespace veranschaulicht:

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"

Namespaces in Dateien und Assemblys

Namespaces können mehrere Dateien in einem einzelnen Projekt oder einer einzelnen Kompilierung umfassen. Der Begriff Namespacefragment beschreibt den Teil eines Namespace, der in einer Datei enthalten ist. Namespaces können auch mehrere Assemblys umfassen. Beispielsweise umfasst der Namespace System die gesamte .NET Framework-Instanz, die viele Assemblys umfasst und zahlreiche geschachtelte Namespaces enthält.

Namespace „global“

Sie verwenden den vordefinierten Namespace global, um Namen im .NET-Namespace der obersten Ebene zu platzieren.

namespace global

type SomeType() =
    member this.SomeMember = 0

Sie können „global“ auch verwenden, um auf den .NET-Namespace der obersten Ebene zu verweisen, z. B. zum Auflösen von Namenskonflikten mit anderen Namespaces.

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

Rekursive Namespaces

Namespaces können auch als rekursiv deklariert werden, damit der gesamte enthaltene Code wechselseitig rekursiv ist. Dies erfolgt über namespace rec. Durch die Verwendung von namespace rec können einige Probleme umgangen werden, die dadurch entstehen, dass kein gegenseitig referenzieller Code zwischen Typen und Modulen geschrieben werden kann. Im Folgenden sehen Sie ein Beispiel dafür:

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

Beachten Sie, dass die Ausnahme DontSqueezeTheBananaException und die Klasse Banana aufeinander verweisen. Darüber hinaus verweisen auch das Modul BananaHelpers und die Klasse Banana aufeinander. Es ist nicht möglich, dies in F# auszudrücken, wenn Sie das Schlüsselwort rec aus dem Namespace MutualReferences entfernen.

Dieses Feature ist auch für Module der obersten Ebene verfügbar.

Weitere Informationen