Propiedades (F#)
Las propiedades son miembros que representan valores asociados a un objeto.
// 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 "tiene" en la programación orientada a objetos, para representar datos asociados a instancias de objeto o, en el caso de las propiedades estáticas, al tipo.
Puede declarar propiedades de dos maneras, dependiendo de si desea especificar explícitamente el valor subyacente (también denominado el almacén de respaldo) para la propiedad, o si desea permitir que el compilador genere automáticamente el almacén de respaldo para usted. Por lo general, debe utilizar la forma más explícita si la propiedad tiene una implementación que no es trivial y la forma automática cuando la propiedad es un simple contenedor para un valor o variable. Para declarar explícitamente una propiedad, utilice la member palabra clave. Esta sintaxis declarativa va seguida de la sintaxis que especifica los métodos get y set, también denominados descriptores de acceso. Las diversas formas de la sintaxis explícita que se muestra en la sección de sintaxis se utilizan para las propiedades de lectura y escritura, de sólo lectura y sólo escritura. Para las propiedades de solo lectura, se define únicamente un método get; para las de solo escritura, solamente se define un método set. Cabe destacar que cuando una propiedad tiene los descriptores de acceso get y set, la sintaxis alternativa permite especificar atributos y modificadores de accesibilidad que son 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 los métodos set y get, el orden de get y set se puede invertir. Como alternativa, se puede proporcionar la sintaxis mostrada para get únicamente y la sintaxis mostrada para set únicamente, en lugar de utilizar la sintaxis combinada. Esto hace que resulte más fácil marcar como comentario el método set o el get individual en caso de que sea necesario 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 para las propiedades se denominan memoria auxiliar. Para hacer que el compilador cree automáticamente el almacén de respaldo, utilice las palabras clave member val, omite la self-identifier, a continuación, proporcionar una expresión para inicializar la propiedad. Si la propiedad es modificable, incluyen with get, set. Por ejemplo, el siguiente tipo de clase incluye dos propiedades implementadas automáticamente. Property1es de sólo lectura y se inicializa en el argumento proporcionado al constructor principal, y Property2 es una propiedad configurable que se inicializa en una cadena vacía:
type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set
Propiedades implementadas automáticamente forman parte de la inicialización de un tipo, por lo que deben incluirse antes de las definiciones de miembros, como let enlaces y do enlaces en una definición de tipo. Tenga en cuenta que sólo se evalúa la expresión que inicializa una propiedad implementada automáticamente tras la inicialización, y no cada vez que se tiene acceso a la propiedad. Este comportamiento es diferente en el comportamiento de una propiedad implementada de forma explícita. Lo que esto significa es que el código para inicializar estas propiedades se agrega al constructor de una clase. Tenga en cuenta el siguiente código 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.AutoProperty = %d" class1.AutoProperty
printfn "class1.ExplicitProperty = %d" class1.ExplicitProperty
printfn "class1.ExplicitProperty = %d" class1.ExplicitProperty
Output
El resultado del código anterior muestra que el valor de AutoProperty no cambia cuando se llama varias veces, mientras que el ExplicitProperty cambia cada vez que se llama. Esto demuestra que la expresión de una propiedad implementada automáticamente no se evalúa cada vez, como es el método Get para la propiedad explícita.
Advertencia
Hay algunas bibliotecas, tales como propiedades implementadas de Entity Framework (System.Data.Entity) que realizan operaciones personalizadas en los constructores de clase base que no funcionan bien con la inicialización de forma automática.En estos casos, pruebe a usar propiedades explícitas.
Las propiedades pueden ser miembros de clases, estructuras, uniones discriminadas, registros, interfaces y extensiones de tipo; además, se pueden definir en expresiones de objeto.
Se pueden aplicar atributos a las propiedades. Para aplicar un atributo a una propiedad, se debe escribir el atributo en una línea independiente antes de la propiedad. Para obtener más información, vea Atributos (F#).
De manera predeterminada, las propiedades son públicas. Los modificadores de accesibilidad también se pueden aplicar a las propiedades. Para aplicar un modificador de accesibilidad, se agrega inmediatamente antes del nombre de la propiedad en caso de que deba aplicarse a los métodos set y get; o bien antes de las palabras clave set y get si se necesita una accesibilidad distinta para cada descriptor de acceso. accessibility-modifier puede ser uno de los siguientes: public, private o internal. Para obtener más información, vea Control de acceso (F#).
Las implementaciones de propiedad se ejecutan cada vez que se obtiene acceso 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 utilizan para los valores asociados al tipo, no a objetos individuales. Para las propiedades estáticas, omita el self-identifier. El self-identifier se requiere para las propiedades de instancia.
La definición de propiedad estática siguiente está basada en un escenario en el que hay un campo myStaticValue estático que es la memoria auxiliar para la propiedad.
static member MyStaticProperty
with get() = myStaticValue
and set(value) = myStaticValue <- value
Las propiedades también pueden ser como una matriz, en cuyo caso se denominan propiedades indizadas. Para obtener más información, vea Propiedades indizadas (F#).
Anotación de tipo para las propiedades
En muchos casos, el compilador dispone de información suficiente para realizar la inferencia de tipos de una propiedad a partir del tipo de memoria auxiliar; sin embargo, se puede establecer el tipo explícitamente agregando 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
Utilizar descriptores de acceso set de propiedad
Se pueden 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. Como sucede con los métodos, abstracto significa tan solo que hay una distribución virtual asociada a la propiedad. Las propiedades abstractas pueden ser verdaderamente abstractas, es decir, sin una definición en la misma clase. Así pues, la clase que contiene una propiedad de este tipo es una clase abstracta. Como alternativa, el término abstracto puede significar que una propiedad es virtual, y en ese caso, debe haber una definición presente 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, vea Clases abstractas (F#).
// 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