Explizite Felder: Das val-Schlüsselwort

Das val-Schlüsselwort wird zum Deklarieren eines Speicherort zum Speichern eines Werts in einer Klasse oder einem Strukturtyp verwendet, ohne ihn zu initialisieren. Speicherorte, die auf diese Weise deklariert werden, werden als explizite Felder bezeichnet. Das val-Schlüsselwort kann auch zusammen mit dem member-Schlüsselwort verwendet werden, um eine automatisch implementierte Eigenschaft zu deklarieren. Weitere Informationen zu automatisch implementierten Eigenschaften finden Sie unter Eigenschaften.

Syntax

val [ mutable ] [ access-modifier ] field-name : type-name

Bemerkungen

In der Regel werden Felder in einer Klasse oder einem Strukturtyp durch eine let-Bindung definiert. let-Bindungen müssen jedoch als Teil des Klassenkonstruktors initialisiert werden, was nicht immer möglich, notwendig oder gewünscht ist. Sie können das val-Schlüsselwort verwenden, wenn Sie ein nicht initialisiertes Feld implementieren möchten.

Explizite Felder können statisch oder nicht statisch sein. Der Zugriffsmodifizierer kann public, private oder internal sein. Standardmäßig sind explizite Felder öffentlich. Dies unterscheidet sich von let-Bindungen in Klassen, die immer privat sind.

Das DefaultValue-Attribut ist für explizite Felder in Klassentypen erforderlich, die über einen primären Konstruktor verfügen. Dieses Attribut gibt an, dass das Feld mit 0 (null) initialisiert wird. Der Typ des Felds muss die Initialisierung mit 0 (null) unterstützen. Ein Typ unterstützt die Initialisierung mit 0 (null), wenn er eine der folgenden Bedingungen erfüllt:

  • Ein primitiver Typ, der einen Wert von 0 (null) hat.
  • Ein Typ, der den Wert 0 (null) unterstützt, entweder als ein normaler Wert, als ein nicht normaler Wert oder als Darstellung eines Werts. Dazu gehören Klassen, Tupel, Datensätze, Funktionen, Schnittstellen, .NET-Verweistypen, der unit-Typ und diskriminierte Union-Typen.
  • Ein .NET-Werttyp.
  • Eine Struktur, deren Felder alle einen Standardwert von 0 (null) unterstützen.

Ein unveränderliches Feld mit der Bezeichnung someField verfügt zum Beispiel über ein zugrunde liegendes Feld in der kompilierten .NET-Darstellung, das someField@ heißt, und Sie greifen auf den gespeicherten Wert mit einer Eigenschaft mit der Bezeichnung someField zu.

Bei einem veränderlichen Feld ist die kompilierte .NET-Darstellung ein .NET-Feld.

Warnung

Der .NET Framework-Namespace System.ComponentModel enthält ein Attribut mit dem gleichen Namen. Informationen zu diesem Attribut finden Sie unter DefaultValueAttribute.

Der folgende Code zeigt die Verwendung expliziter Felder und zum Vergleich eine let-Bindung in einer Klasse, die über einen primären Konstruktor verfügt. Beachten Sie, dass das let-gebundene Feld myInt1 privat ist. Wenn das let-gebundene Feld myInt1 von einer Membermethode referenziert wird, ist der Selbstbezeichner this nicht erforderlich. Wenn Sie jedoch die expliziten Felder myInt2 und myString referenzieren, ist der Selbstbezeichner erforderlich.

type MyType() =
    let mutable myInt1 = 10
    [<DefaultValue>] val mutable myInt2 : int
    [<DefaultValue>] val mutable myString : string
    member this.SetValsAndPrint( i: int, str: string) =
       myInt1 <- i
       this.myInt2 <- i + 1
       this.myString <- str
       printfn "%d %d %s" myInt1 (this.myInt2) (this.myString)

let myObject = new MyType()
myObject.SetValsAndPrint(11, "abc")
// The following line is not allowed because let bindings are private.
// myObject.myInt1 <- 20
myObject.myInt2 <- 30
myObject.myString <- "def"

printfn "%d %s" (myObject.myInt2) (myObject.myString)

Die Ausgabe lautet wie folgt:

11 12 abc
30 def

Der folgende Code zeigt die Verwendung von expliziten Feldern in einer Klasse, die nicht über einen primären Konstruktor verfügt. In diesem Fall ist das DefaultValue-Attribut nicht erforderlich, es müssen jedoch alle Felder in den Konstruktoren initialisiert werden, die für den Typ definiert sind.

type MyClass =
    val a : int
    val b : int
    // The following version of the constructor is an error
    // because b is not initialized.
    // new (a0, b0) = { a = a0; }
    // The following version is acceptable because all fields are initialized.
    new(a0, b0) = { a = a0; b = b0; }

let myClassObj = new MyClass(35, 22)
printfn "%d %d" (myClassObj.a) (myClassObj.b)

Die Ausgabe lautet 35 22.

Der folgende Code zeigt die Verwendung von expliziten Feldern in einer Struktur. Weil eine Struktur ein Werttyp ist, verfügt sie automatisch über einen parameterlosen Standardkonstruktor, der die Werte der zugehörigen Felder auf 0 (null) festlegt. Daher ist das DefaultValue-Attribut nicht erforderlich.

type MyStruct =
    struct
        val mutable myInt : int
        val mutable myString : string
    end

let mutable myStructObj = new MyStruct()
myStructObj.myInt <- 11
myStructObj.myString <- "xyz"

printfn "%d %s" (myStructObj.myInt) (myStructObj.myString)

Die Ausgabe lautet 11 xyz.

Vorsicht: Wenn Sie Ihre Struktur mit mutable-Feldern ohne das Schlüsselwort mutable initialisieren, funktionieren Ihre Zuweisungen mit einer Kopie der Struktur, die direkt nach der Zuweisung verworfen wird. Daher ändert sich Ihre Struktur nicht.

[<Struct>]
type Foo =
    val mutable bar: string
    member self.ChangeBar bar = self.bar <- bar
    new (bar) = {bar = bar}

let foo = Foo "1"
foo.ChangeBar "2" //make implicit copy of Foo, changes the copy, discards the copy, foo remains unchanged
printfn "%s" foo.bar //prints 1

let mutable foo' = Foo "1"
foo'.ChangeBar "2" //changes foo'
printfn "%s" foo'.bar //prints 2

Explizite Felder sind nicht für die routinemäßige Verwendung gedacht. Im Allgemeinen sollten Sie möglichst eine let-Bindung in einer Klasse verwenden, statt eines expliziten Felds. Explizite Felder sind in bestimmten Interoperabilitätsszenarios hilfreich, wenn Sie z. B. eine Struktur definieren müssen, die in einem Plattformaufruf einer systemeigenen API oder in COM-Interop-Szenarien verwendet wird. Weitere Informationen finden Sie unter Externe Funktionen. Eine andere Situation, in der ein explizites Feld möglicherweise erforderlich, besteht dann, wenn Sie mit einem F#-Codegenerator arbeiten, der Klassen ohne primären Konstruktor ausgibt. Explizite Felder sind auch für threadstatische Variablen oder ähnliche Konstrukte sinnvoll. Weitere Informationen finden Sie unter System.ThreadStaticAttribute.

Wenn die Schlüsselwörter member val zusammen in einer Typdefinition angezeigt werden, handelt es sich um eine Definition einer automatisch implementierten Eigenschaft. Weitere Informationen finden Sie unter Eigenschaftendefiniert sind.

Siehe auch