Campos explícitos: palabra clave val

La palabra clave val se usa para declarar una ubicación para almacenar un valor en un tipo de clase o estructura sin inicializarlo. Las ubicaciones de almacenamiento que se declaran de esta manera se conocen como campos explícitos. Otra manera de usar la palabra clave val es conjuntamente con la palabra clave member para declarar una propiedad implementada automáticamente. Para obtener más información sobre las propiedades implementadas automáticamente, vea Propiedades.

Sintaxis

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

Comentarios

La forma más habitual de definir campos en un tipo de clase o estructura es usar un enlace let. Sin embargo, los enlaces let deben inicializarse como parte del constructor de clase, lo que no siempre es posible, necesario o deseable. Puede usar la palabra clave val cuando quiera un campo que no se haya inicializado.

Los campos explícitos pueden ser estáticos o no estáticos. El modificador de acceso puede ser public, private o internal. De forma predeterminada, los campos explícitos son públicos. Esto difiere de los enlaces let de las clases, que siempre son privados.

El atributo DefaultValue es necesario en los campos explícitos de los tipos de clase que tienen un constructor primario. Este atributo especifica que el campo se inicializa en cero. El tipo del campo debe admitir la inicialización en cero. Los tipos que admiten la inicialización en cero son los siguientes:

  • Un tipo primitivo que tenga un valor de cero.
  • Un tipo que admita un valor nulo, ya sea como valor normal, como valor anormal o como representación de un valor. Esto incluye clases, tuplas, registros, funciones, interfaces, tipos de referencia .NET, el tipo unit y tipos de unión discriminada.
  • Un tipo de valor. NET.
  • Una estructura cuyos campos admitan el valor cero predeterminado.

Por ejemplo, un campo inmutable denominado someField tiene un campo de respaldo en la representación compilada de .NET con el nombre someField@ y el usuario accede al valor almacenado con una propiedad denominada someField.

Para un campo mutable, la representación compilada de .NET es un campo .NET.

Advertencia

El espacio de nombres System.ComponentModel de .NET Framework contiene un atributo que tiene el mismo nombre. Para obtener más información sobre el atributo, vea DefaultValueAttribute.

El código siguiente muestra el uso de campos explícitos y, para la comparación, un let de enlace en una clase que tiene un constructor primario. Tenga en cuenta que el campo enlazado a letmyInt1 es privado. Si se hace referencia al campo enlazado a letmyInt1 desde un método de miembro, el identificador propio this no es necesario. Pero si hace referencia a los campos explícitos myInt2 y myString, se requiere el identificador propio.

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)

La salida es como sigue:

11 12 abc
30 def

En el código siguiente se muestra el uso de campos explícitos en una clase que no tiene constructor primario. En este caso, el atributo DefaultValue no es necesario, pero todos los campos deben estar inicializados en los constructores definidos para el 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)

La salida es 35 22.

En el código siguiente se muestra el uso de campos explícitos en una estructura. Como una estructura es un tipo de valor, automáticamente tiene un constructor sin parámetros que establece los valores de sus campos en cero. Por lo tanto, el atributo DefaultValue no es necesario.

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)

La salida es 11 xyz.

Tenga en cuenta que, si va a inicializar la estructura con campos mutable sin palabra clave mutable, las asignaciones funcionarán en una copia de la estructura que se descartará justo después de la asignación. Por lo tanto, la estructura no cambiará.

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

Los campos explícitos no están pensados para un uso rutinario. En general, siempre que sea posible debe usar un enlace let en una clase en lugar de un campo explícito. Los campos explícitos son útiles en determinados escenarios de interoperabilidad, como cuando se necesita definir una estructura que se usará en una llamada de invocación a una plataforma API nativa o en escenarios de interoperabilidad COM. Para obtener más información, vea Funciones externas. Otra situación en la que podría ser necesario un campo explícito es cuando se trabaja con un generador de código F # que emite clases sin un constructor primario. Los campos explícitos también son útiles para las variables de subproceso estático o construcciones similares. Para más información, consulte System.ThreadStaticAttribute.

Cuando las palabras clave member val aparecen juntas en una definición de tipo, se trata de la definición de una propiedad implementada automáticamente. Para obtener más información, consulta Propiedades.

Consulte también