Campos explícitos: a palavra-chave val
A palavra-chave val
é usada para declarar um local para armazenar um valor em um tipo de classe ou de estrutura, sem inicializá-lo. Os locais de armazenamento declarados dessa maneira são chamados de campos explícitos. Outro uso da val
palavra-chave é em conjunto com a member
palavra-chave para declarar uma propriedade implementada automaticamente. Para obter mais informações sobre propriedades implementadas automaticamente, consulte Propriedades.
Sintaxe
val [ mutable ] [ access-modifier ] field-name : type-name
Comentários
A maneira usual de definir campos em um tipo de classe ou de estrutura é usar uma associação let
. No entanto, as associações let
devem ser inicializadas como parte do construtor de classe, o que nem sempre é possível, necessário ou desejável. Você pode usar a palavra-chave val
quando quiser um campo não inicializado.
Campos explícitos podem ser estáticos ou não estáticos. O modificador de acesso pode ser public
, private
ou internal
. Por padrão, campos explícitos são públicos. Isso difere das associações let
nas classes, que são sempre privadas.
O atributo DefaultValue é necessário nos campos explícitos em tipos de classe que têm um construtor primário. Esse atributo especifica que o campo é inicializado como zero. O tipo do campo deve dar suporte à inicialização zero. Um tipo dá suporte à inicialização zero se for um dos seguintes:
- Um tipo primitivo que tem um valor zero.
- Um tipo que dá suporte a um valor nulo, seja como um valor normal, como um valor anormal ou como uma representação de um valor. Isso inclui classes, tuplas, registros, funções, interfaces, tipos de referência do .NET, o tipo
unit
e tipos de união discriminados. - Um tipo de valor do .NET.
- Uma estrutura cujos campos dão suporte a um valor zero padrão.
Por exemplo, um campo imutável chamado someField
tem um campo de suporte na representação compilada do .NET com o nome someField@
e você acessa o valor armazenado usando uma propriedade chamada someField
.
Para um campo mutável, a representação compilada do .NET é um campo do .NET.
Aviso
O namespace do .NET Framework System.ComponentModel
contém um atributo que tem o mesmo nome. Para obter mais informações sobre esse atributo, consulte DefaultValueAttribute.
O código a seguir mostra o uso de campos explícitos e, para comparação, uma associação let
em uma classe que tem um construtor primário. Observe que o campo myInt1
associado ao let
é privado. Quando o campo myInt1
associado ao let
é referenciado de um método membro, o auto-identificador this
não é necessário. Mas quando você está fazendo referência aos campos explícitos myInt2
e myString
, o auto-identificador é necessário.
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)
A saída é da seguinte maneira:
11 12 abc
30 def
O código a seguir mostra o uso de campos explícitos em uma classe que não tem um construtor primário. Nesse caso, o atributo DefaultValue
não é necessário, mas todos os campos devem ser inicializados nos construtores definidos para o tipo.
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)
A saída é 35 22
.
O código a seguir mostra o uso de campos explícitos em uma estrutura. Como uma estrutura é um tipo de valor, ela tem automaticamente um construtor sem parâmetros que define os valores de seus campos como zero. Portanto, o atributo DefaultValue
não é necessário.
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)
A saída é 11 xyz
.
Cuidado, se você vai inicializar sua estrutura com campos mutable
sem a palavra-chave mutable
, suas atribuições funcionarão em uma cópia da estrutura que será descartada logo após a atribuição. Portanto, sua estrutura não será alterada.
[<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
Campos explícitos não se destinam ao uso rotineiro. Em geral, quando possível, você deve usar uma associação let
em uma classe em vez de um campo explícito. Campos explícitos são úteis em determinados cenários de interoperabilidade, como quando você precisa definir uma estrutura que será usada em uma chamada de invocação de plataforma para uma API nativa ou em cenários de interoperabilidade COM. Para saber mais, consulte Funções externas. Outra situação em que um campo explícito pode ser necessário é quando você está trabalhando com um gerador de código F# que emite classes sem um construtor primário. Campos explícitos também são úteis para variáveis estáticas de thread ou constructos semelhantes. Para obter mais informações, consulte System.ThreadStaticAttribute
.
Quando as palavras-chave member val
aparecem juntas em uma definição de tipo, ela é uma definição de uma propriedade implementada automaticamente. Para obter mais informações, consulte Propriedades.