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
, private
eller 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 myString
krä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.