Campos explícitos: A palavra-chave val
A val
palavra-chave é usada para declarar um local para armazenar um valor em uma classe ou tipo 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
Observações
A maneira usual de definir campos em um tipo de classe ou estrutura é usar uma let
ligação. No entanto, let
as ligações devem ser inicializadas como parte do construtor de classe, o que nem sempre é possível, necessário ou desejável. Você pode usar a val
palavra-chave quando quiser um campo não inicializado.
Os 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, os campos explícitos são públicos. Isso difere das let
ligações nas classes, que são sempre privadas.
O atributo DefaultValue é necessário em campos explícitos em tipos de classe que têm um construtor primário. Este atributo especifica que o campo é inicializado como zero. O tipo de campo deve suportar inicialização zero. Um tipo suporta inicialização zero se for um dos seguintes:
- Um tipo primitivo que tem um valor zero.
- Um tipo que suporta um valor nulo, 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 .NET, o
unit
tipo e tipos de união discriminados. - Um tipo de valor .NET.
- Uma estrutura cujos campos suportam 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 .NET.
Aviso
O namespace System.ComponentModel
do .NET Framework contém um atributo que tem o mesmo nome. Para obter informações sobre esse atributo, consulte DefaultValueAttribute.
O código a seguir mostra o uso de campos explícitos e, para comparação, uma let
associação em uma classe que tem um construtor primário. Observe que o let
campo myInt1
-bound é privado. Quando o let
campo -bound é referenciado a partir de um método membro, o autoidentificador this
myInt1
não é necessário. Mas quando você está fazendo referência aos campos myInt2
explícitos e myString
, o autoidentificador é 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)
O resultado é o seguinte:
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 DefaultValue
atributo não é obrigató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)
O resultado é 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 DefaultValue
atributo 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)
O resultado é 11 xyz
.
Cuidado, se você vai inicializar sua estrutura com mutable
campos sem mutable
palavra-chave, 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 mudará.
[<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
Os campos explícitos não se destinam a utilização de rotina. Em geral, quando possível, você deve usar uma let
associação em uma classe em vez de um campo explícito. Os 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 obter mais informações, 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 construções semelhantes. Para obter mais informações, veja System.ThreadStaticAttribute
.
Quando as palavras-chave member val
aparecem juntas em uma definição de tipo, é uma definição de uma propriedade implementada automaticamente. Para obter mais informações, consulte Propriedades.