Propiedades (F#)

Las propiedades son miembros que representan valores asociados a un objeto.

Sintaxis

// Property that has both get and set defined.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with [accessibility-modifier] get() =
    get-function-body
and [accessibility-modifier] set parameter =
    set-function-body

// Alternative syntax for a property that has get and set.
[ attributes-for-get ]
[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName =
    get-function-body
[ attributes-for-set ]
[ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName
with set parameter =
    set-function-body

// Property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName =
    get-function-body

// Alternative syntax for property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with get() =
    get-function-body

// Property that has set only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with set parameter =
    set-function-body

// Automatically implemented properties.
[ attributes ]
[ static ] member val [accessibility-modifier] PropertyName = initialization-expression [ with get, set ]

Comentarios

Las propiedades representan la relación de lo que se tiene en la programación orientada a objetos, que representa los datos asociados a instancias de objeto o, en el caso de las propiedades estáticas, al tipo.

Puede declarar propiedades de dos maneras, en función de si quiere especificar explícitamente el valor subyacente (también denominado "memoria auxiliar") de la propiedad o si quiere permitir que el compilador genere automáticamente la memoria auxiliar. Por lo general, debe usar la forma más explícita si la propiedad tiene una implementación no trivial, y la forma automática cuando la propiedad no es más que un contenedor simple para un valor o variable. Para declarar una propiedad explícitamente, use la palabra clave member. Esta sintaxis declarativa va seguida de la sintaxis que especifica los métodos get y set, también denominados descriptores de acceso. Las distintas formas de la sintaxis explícita que se muestran en la sección de sintaxis se usan para propiedades de solo lectura y escritura, de solo lectura y de solo escritura. Para las propiedades de solo lectura, se define solo un método get; para las de solo escritura, se define solo un método set. Tenga en cuenta que, cuando una propiedad tiene descriptores de acceso get y set, la sintaxis alternativa permite especificar atributos y modificadores de accesibilidad diferentes para cada descriptor de acceso, como se muestra en el código siguiente.

// A read-only property.
member this.MyReadOnlyProperty = myInternalValue
// A write-only property.
member this.MyWriteOnlyProperty with set (value) = myInternalValue <- value
// A read-write property.
member this.MyReadWriteProperty
    with get () = myInternalValue
    and set (value) = myInternalValue <- value

Para las propiedades de lectura y escritura, que tienen un método get y set, se puede invertir el orden de get y set. Como alternativa, puede proporcionar la sintaxis que se muestra solo para get y la sintaxis que se muestra solo para set, en lugar de usar la sintaxis combinada. De esta manera, resulta más fácil comentar el método get o set individual, si necesita hacerlo. Esta alternativa al uso de la sintaxis combinada se muestra en el código siguiente.

member this.MyReadWriteProperty with get () = myInternalValue
member this.MyReadWriteProperty with set (value) = myInternalValue <- value

Los valores privados que contienen los datos de las propiedades se denominan memoria auxiliar. Para que el compilador cree automáticamente la memoria auxiliar, use las palabras clave member val, omita el identificador propio y proporcione una expresión para inicializar la propiedad. Si la propiedad debe ser mutable, incluya with get, set. Por ejemplo, el siguiente tipo de clase incluye dos propiedades implementadas automáticamente. Property1 es de solo lectura y se inicializa en el argumento proporcionado al constructor principal, mientras que Property2 es una propiedad configurable inicializada en una cadena vacía:

type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set

Las propiedades implementadas automáticamente forman parte de la inicialización de un tipo, por lo que deben incluirse antes de cualquier otra definición de miembro, igual que los enlaces let y los enlaces do de una definición de tipo. Tenga en cuenta que la expresión que inicializa una propiedad implementada automáticamente solo se evalúa tras la inicialización, y no cada vez que se accede a la propiedad. Este comportamiento contrasta con el comportamiento de una propiedad implementada explícitamente. Lo que esto conlleva efectivamente es que el código para inicializar estas propiedades se agrega al constructor de una clase. Observe el código siguiente, en el que se muestra esta diferencia:

type MyClass() =
    let random  = new System.Random()
    member val AutoProperty = random.Next() with get, set
    member this.ExplicitProperty = random.Next()

let class1 = new MyClass()

printfn $"class1.AutoProperty = %d{class1.AutoProperty}"
printfn $"class1.ExplicitProperty = %d{class1.ExplicitProperty}"

Salida

class1.AutoProperty = 1853799794
class1.AutoProperty = 1853799794
class1.ExplicitProperty = 978922705
class1.ExplicitProperty = 1131210765

La salida del código anterior muestra que el valor de AutoProperty no cambia cuando se le llama repetidamente, mientras que ExplicitProperty cambia cada vez que se le llama. Esto demuestra que la expresión de una propiedad implementada automáticamente no se evalúa cada vez, a diferencia del método captador de la propiedad explícita.

Advertencia

Hay algunas bibliotecas, como Entity Framework (System.Data.Entity), que realizan operaciones personalizadas en constructores de clase base que no funcionan bien con la inicialización de propiedades implementadas automáticamente. En esos casos, intente usar propiedades explícitas.

Las propiedades pueden ser miembros de clases, estructuras, uniones discriminadas, registros, interfaces y extensiones de tipo, y también se pueden definir en expresiones de objeto.

Se pueden aplicar atributos a las propiedades. Para aplicar un atributo a una propiedad, escriba el atributo en una línea independiente antes de la propiedad. Para obtener más información, consulte Attributes (Atributos).

De manera predeterminada, las propiedades son públicas. También se pueden aplicar modificadores de accesibilidad a las propiedades. Para aplicar un modificador de accesibilidad, agréguelo inmediatamente antes del nombre de la propiedad si está pensado para aplicarse a los métodos get y set; agréguelo antes de las palabras clave get y set si se requiere una accesibilidad diferente para cada descriptor de acceso. El valor de accessibility-modifier puede ser uno de los siguientes: public, private y internal. Para obtener más información, consulta Access Control.

Las implementaciones de propiedades se ejecutan cada vez que se accede a una propiedad.

Propiedades estáticas y de instancia

Las propiedades pueden ser estáticas o de instancia. Las propiedades estáticas se pueden invocar sin una instancia y se usan para valores asociados al tipo, no a objetos individuales. En el caso de las propiedades estáticas, omita el identificador propio. El identificador propio es necesario para las propiedades de instancia.

La siguiente definición de propiedad estática se basa en un escenario en el que se tiene un campo estático myStaticValue que es la memoria auxiliar de la propiedad.

static member MyStaticProperty
    with get() = myStaticValue
    and set(value) = myStaticValue <- value

Las propiedades también pueden ser similares a una matriz, en cuyo caso se denominan propiedades indexadas. Para obtener más información, consulte Propiedades indexadas.

Anotación de tipo para propiedades

En muchos casos, el compilador dispone de información suficiente para deducir el tipo de una propiedad a partir del tipo de la memoria auxiliar, pero se puede establecer el tipo explícitamente si se agrega una anotación de tipo.

// To apply a type annotation to a property that does not have an explicit
// get or set, apply the type annotation directly to the property.
member this.MyProperty1 : int = myInternalValue
// If there is a get or set, apply the type annotation to the get or set method.
member this.MyProperty2 with get() : int = myInternalValue

Uso de descriptores de acceso set en las propiedades

Puede establecer propiedades que proporcionen descriptores de acceso set mediante el operador <-.

// Assume that the constructor argument sets the initial value of the
// internal backing store.
let mutable myObject = new MyType(10)
myObject.MyProperty <- 20
printfn "%d" (myObject.MyProperty)

El resultado es 20.

Propiedades abstractas

Las propiedades pueden ser abstractas. Igual que en el caso de los métodos, abstract simplemente significa que hay una distribución virtual asociada a la propiedad. Las propiedades abstractas pueden ser completamente abstractas, es decir, sin una definición en la misma clase. Por lo tanto, la clase que contiene dicha propiedad es una clase abstracta. "Abstracto" también puede significar simplemente que una propiedad es virtual y, en ese caso, debe haber presente una definición en la misma clase. Tenga en cuenta que las propiedades abstractas no deben ser privadas y, si un descriptor de acceso es abstracto, el otro también debe serlo. Para obtener más información sobre las clases abstractas, consulte Clases abstractas.

// Abstract property in abstract class.
// The property is an int type that has a get and
// set method
[<AbstractClass>]
type AbstractBase() =
    abstract Property1: int with get, set

// Implementation of the abstract property
type Derived1() =
    inherit AbstractBase()
    let mutable value = 10

    override this.Property1
        with get () = value
        and set (v: int) = value <- v

// A type with a "virtual" property.
type Base1() =
    let mutable value = 10
    abstract Property1: int with get, set

    default this.Property1
        with get () = value
        and set (v: int) = value <- v

// A derived type that overrides the virtual property
type Derived2() =
    inherit Base1()
    let mutable value2 = 11

    override this.Property1
        with get () = value2
        and set (v) = value2 <- v

Consulte también