Explicita fält: Val-nyckelordet

Nyckelordet val används för att deklarera en plats för att lagra ett värde i en klass eller strukturtyp, utan att initiera det. Lagringsplatser som deklareras på det här sättet kallas explicita fält. En annan användning av nyckelordet val är tillsammans med nyckelordet member för att deklarera en automatiskt implementerad egenskap. Mer information om automatiskt implementerade egenskaper finns i Egenskaper.

Syntax

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

Kommentarer

Det vanliga sättet att definiera fält i en klass eller strukturtyp är att använda en let bindning. Bindningar måste dock let initieras som en del av klasskonstruktorn, vilket inte alltid är möjligt, nödvändigt eller önskvärt. Du kan använda nyckelordet val när du vill ha ett fält som är oinitierat.

Explicita fält kan vara statiska eller icke-statiska. Åtkomstmodifieraren kan vara public, privateeller internal. Som standard är explicita fält offentliga. Detta skiljer sig från let bindningar i klasser, som alltid är privata.

Attributet DefaultValue krävs för explicita fält i klasstyper som har en primär konstruktor. Det här attributet anger att fältet initieras till noll. Typen av fält måste ha stöd för nollinitiering. En typ stöder nollinitiering om det är något av följande:

  • En primitiv typ som har ett nollvärde.
  • En typ som stöder ett null-värde, antingen som ett normalvärde, som ett onormalt värde eller som en representation av ett värde. Detta omfattar klasser, tupplar, poster, funktioner, gränssnitt, .NET-referenstyper, unit typ och diskriminerade unionstyper.
  • En .NET-värdetyp.
  • En struktur vars fält stöder ett standardvärde på noll.

Ett oföränderligt fält med namnet someField har till exempel ett bakgrundsfält i den .NET-kompilerade representationen med namnet someField@och du får åtkomst till det lagrade värdet med hjälp av en egenskap med namnet someField.

För ett föränderligt fält är den .NET-kompilerade representationen ett .NET-fält.

Varning

.NET Framework-namnområdet System.ComponentModel innehåller ett attribut som har samma namn. Information om det här attributet finns i DefaultValueAttribute.

Följande kod visar användningen av explicita fält och, som jämförelse, en let bindning i en klass som har en primär konstruktor. Observera att fältet let-bound myInt1 är privat. När fältet let-bound myInt1 refereras från en medlemsmetod krävs inte självidentifieraren this . Men när du refererar till de explicita fälten myInt2 och myStringkrävs självidentifieraren.

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)

Utdata är följande:

11 12 abc
30 def

Följande kod visar användningen av explicita fält i en klass som inte har någon primär konstruktor. I det här fallet DefaultValue krävs inte attributet, men alla fält måste initieras i konstruktorerna som har definierats för typen.

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)

Utdata är 35 22.

Följande kod visar användningen av explicita fält i en struktur. Eftersom en struktur är en värdetyp har den automatiskt en parameterlös konstruktor som anger värdena för fälten till noll. Därför DefaultValue krävs inte attributet.

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)

Utdata är 11 xyz.

Tänk på att om du ska initiera din struktur med mutable fält utan mutable nyckelord fungerar dina tilldelningar på en kopia av strukturen som tas bort direkt efter tilldelningen. Därför ändras inte strukturen.

[<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

Explicita fält är inte avsedda för rutinmässig användning. I allmänhet bör du när det är möjligt använda en let bindning i en klass i stället för ett explicit fält. Explicita fält är användbara i vissa samverkansscenarier, till exempel när du behöver definiera en struktur som ska användas i en plattform som anropar anrop till ett internt API eller i COM-interopscenarier. Mer information finns i Externa funktioner. En annan situation där ett explicit fält kan vara nödvändigt är när du arbetar med en F#-kodgenerator som genererar klasser utan en primär konstruktor. Explicita fält är också användbara för trådstatiska variabler eller liknande konstruktioner. Mer information finns i System.ThreadStaticAttribute.

När nyckelorden member val visas tillsammans i en typdefinition är det en definition av en automatiskt implementerad egenskap. Mer information finns i Egenskaper.

Se även