Expliciete velden: het trefwoord val
Het val
trefwoord wordt gebruikt om een locatie te declareren voor het opslaan van een waarde in een klasse- of structuurtype, zonder deze te initialiseren. Opslaglocaties die op deze manier worden gedeclareerd, worden expliciete velden genoemd. Een ander gebruik van het val
trefwoord is in combinatie met het member
trefwoord om een automatisch geïmplementeerde eigenschap te declareren. Zie Eigenschappen voor meer informatie over automatisch geïmplementeerde eigenschappen.
Syntaxis
val [ mutable ] [ access-modifier ] field-name : type-name
Opmerkingen
De gebruikelijke manier om velden in een klasse- of structuurtype te definiëren, is door een let
binding te gebruiken. let
Bindingen moeten echter worden geïnitialiseerd als onderdeel van de klasseconstructor, die niet altijd mogelijk, noodzakelijk of wenselijk is. U kunt het val
trefwoord gebruiken als u een veld wilt dat niet-geïnitialiseerd is.
Expliciete velden kunnen statisch of niet-statisch zijn. De toegangsaanpassing kan , public
private
of internal
. Expliciete velden zijn standaard openbaar. Dit verschilt van let
bindingen in klassen, die altijd privé zijn.
Het kenmerk DefaultValue is vereist voor expliciete velden in klassetypen met een primaire constructor. Dit kenmerk geeft aan dat het veld wordt geïnitialiseerd tot nul. Het type veld moet ondersteuning bieden voor nul-initialisatie. Een type ondersteunt nul-initialisatie als dit een van de volgende is:
- Een primitief type met een nulwaarde.
- Een type dat een null-waarde ondersteunt, ofwel als een normale waarde, als een abnormale waarde of als een weergave van een waarde. Dit omvat klassen, tuples, records, functies, interfaces, .NET-referentietypen, het
unit
type en gediscrimineerde samenvoegingstypen. - Een .NET-waardetype.
- Een structuur waarvan alle velden een standaard nulwaarde ondersteunen.
Een onveranderbaar veld met de naam someField
heeft bijvoorbeeld een back-upveld in de .NET-gecompileerde weergave met de naam someField@
en u opent de opgeslagen waarde met behulp van een eigenschap met de naam someField
.
Voor een veranderlijk veld is de gecompileerde .NET-weergave een .NET-veld.
Waarschuwing
De .NET Framework-naamruimte System.ComponentModel
bevat een kenmerk met dezelfde naam. Zie DefaultValueAttributevoor meer informatie over dit kenmerk.
De volgende code toont het gebruik van expliciete velden en, ter vergelijking, een let
binding in een klasse met een primaire constructor. Houd er rekening mee dat het let
veld -gebonden myInt1
privé is. Wanneer er vanuit een lidmethode naar het let
veld myInt1
-bound wordt verwezen, is de self-id this
niet vereist. Maar wanneer u verwijst naar de expliciete velden myInt2
en myString
, is de self-id vereist.
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)
De uitvoer is als volgt:
11 12 abc
30 def
De volgende code toont het gebruik van expliciete velden in een klasse die geen primaire constructor heeft. In dit geval is het DefaultValue
kenmerk niet vereist, maar moeten alle velden worden geïnitialiseerd in de constructors die zijn gedefinieerd voor het type.
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)
De uitvoer is 35 22
.
De volgende code toont het gebruik van expliciete velden in een structuur. Omdat een structuur een waardetype is, heeft deze automatisch een constructor zonder parameters waarmee de waarden van de velden worden ingesteld op nul. Daarom is het DefaultValue
kenmerk niet vereist.
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)
De uitvoer is 11 xyz
.
Pas op, als u uw structuur mutable
met velden zonder mutable
trefwoord gaat initialiseren, werken uw toewijzingen aan een kopie van de structuur die direct na de toewijzing wordt verwijderd. Daarom verandert uw structuur niet.
[<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
Expliciete velden zijn niet bedoeld voor routinegebruik. Over het algemeen moet u, indien mogelijk, een let
binding in een klasse gebruiken in plaats van een expliciet veld. Expliciete velden zijn handig in bepaalde interoperabiliteitsscenario's, zoals wanneer u een structuur moet definiëren die wordt gebruikt in een aanroep van het platform naar een systeemeigen API of in COM-interop-scenario's. Zie Externe functies voor meer informatie. Een andere situatie waarin een expliciet veld mogelijk nodig is, is wanneer u met een F#-codegenerator werkt die klassen zonder primaire constructor verzendt. Expliciete velden zijn ook handig voor thread-statische variabelen of vergelijkbare constructies. Zie System.ThreadStaticAttribute
voor meer informatie.
Wanneer de trefwoorden member val
samen worden weergegeven in een typedefinitie, is het een definitie van een automatisch geïmplementeerde eigenschap. Zie Eigenschappen voor meer informatie.