Propriedades (F#)
As propriedades são membros que representam valores associados a um objeto.
Sintaxe
// 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 ]
Observações
As propriedades representam a relação "tem uma" na programação orientada a objetos, representando dados associados a instâncias de objeto ou, para propriedades estáticas, ao tipo.
Você pode declarar propriedades de duas maneiras, dependendo se deseja especificar explicitamente o valor subjacente (também chamado de armazenamento de suporte) para a propriedade ou se deseja permitir que o compilador gere automaticamente o armazenamento de suporte para você. Geralmente, você deve usar a maneira mais explícita se a propriedade tiver uma implementação não trivial e a maneira automática quando a propriedade for apenas um wrapper simples para um valor ou variável. Para declarar uma propriedade explicitamente, use a member
palavra-chave. Essa sintaxe declarativa é seguida pela sintaxe que especifica os get
métodos e set
, também chamados de acessadores. As várias formas da sintaxe explícita mostradas na seção de sintaxe são usadas para propriedades de leitura/gravação, somente leitura e somente gravação. Para propriedades somente leitura, você define apenas um método, para propriedades somente gravação get
, define apenas um set
método. Observe que quando uma propriedade tem ambos e get
set
acessadores, a sintaxe alternativa permite especificar atributos e modificadores de acessibilidade que são diferentes para cada acessador, como é mostrado no código a seguir.
// 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 propriedades de leitura/gravação, que têm um get
e set
método, a ordem de get
e set
pode ser invertida. Como alternativa, você pode fornecer a sintaxe mostrada apenas para get
e a sintaxe mostrada para set
somente em vez de usar a sintaxe combinada. Fazer isso torna mais fácil comentar o indivíduo get
ou set
método, se isso é algo que você pode precisar fazer. Essa alternativa ao uso da sintaxe combinada é mostrada no código a seguir.
member this.MyReadWriteProperty with get () = myInternalValue
member this.MyReadWriteProperty with set (value) = myInternalValue <- value
Os valores privados que contêm os dados das propriedades são chamados de repositórios de backup. Para que o compilador crie o armazenamento de suporte automaticamente, use as palavras-chave member val
, omita o autoidentificador e, em seguida, forneça uma expressão para inicializar a propriedade. Se a propriedade for mutável, inclua with get, set
. Por exemplo, o seguinte tipo de classe inclui duas propriedades implementadas automaticamente. Property1
é somente leitura e é inicializado para o argumento fornecido ao construtor primário, e Property2
é uma propriedade settable inicializada para uma cadeia de caracteres vazia:
type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set
As propriedades implementadas automaticamente fazem parte da inicialização de um tipo, portanto, elas devem ser incluídas antes de qualquer outra definição de membro, assim como let
associações e do
associações em uma definição de tipo. Observe que a expressão que inicializa uma propriedade implementada automaticamente só é avaliada após a inicialização, e não toda vez que a propriedade é acessada. Esse comportamento contrasta com o comportamento de uma propriedade explicitamente implementada. O que isso efetivamente significa é que o código para inicializar essas propriedades é adicionado ao construtor de uma classe. Considere o seguinte código que mostra essa diferença:
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}"
Saída
class1.AutoProperty = 1853799794
class1.AutoProperty = 1853799794
class1.ExplicitProperty = 978922705
class1.ExplicitProperty = 1131210765
A saída do código anterior mostra que o valor de AutoProperty é inalterado quando chamado repetidamente, enquanto o ExplicitProperty muda cada vez que é chamado. Isso demonstra que a expressão para uma propriedade implementada automaticamente não é avaliada cada vez, assim como o método getter para a propriedade explícita.
Aviso
Existem algumas bibliotecas, como o Entity Framework (System.Data.Entity
) que executam operações personalizadas em construtores de classe base que não funcionam bem com a inicialização de propriedades implementadas automaticamente. Nesses casos, tente usar propriedades explícitas.
As propriedades podem ser membros de classes, estruturas, uniões discriminadas, registros, interfaces e extensões de tipo e também podem ser definidas em expressões de objeto.
Os atributos podem ser aplicados às propriedades. Para aplicar um atributo a uma propriedade, escreva o atributo em uma linha separada antes da propriedade. Para obter mais informações, consulte Atributos.
Por padrão, as propriedades são públicas. Os modificadores de acessibilidade também podem ser aplicados às propriedades. Para aplicar um modificador de acessibilidade, adicione-o imediatamente antes do nome da propriedade, se for para se aplicar aos get
métodos e set
e adicione-o antes das get
palavras-chave e set
se for necessária uma acessibilidade diferente para cada acessador. O modificador de acessibilidade pode ser um dos seguintes: public
, private
, internal
. Para obter mais informações, consulte Controle de acesso.
As implementações de propriedade são executadas cada vez que uma propriedade é acessada.
Propriedades estáticas e de instância
As propriedades podem ser estáticas ou de instância. As propriedades estáticas podem ser invocadas sem uma instância e são usadas para valores associados ao tipo, não a objetos individuais. Para propriedades estáticas, omita o autoidentificador. O autoidentificador é necessário para propriedades de exemplo.
A definição de propriedade estática a seguir é baseada em um cenário no qual você tem um campo myStaticValue
estático que é o armazenamento de suporte para a propriedade.
static member MyStaticProperty
with get() = myStaticValue
and set(value) = myStaticValue <- value
As propriedades também podem ser semelhantes a matrizes, caso em que são chamadas de propriedades indexadas. Para obter mais informações, consulte Propriedades indexadas.
Anotação de tipo para propriedades
Em muitos casos, o compilador tem informações suficientes para inferir o tipo de uma propriedade a partir do tipo do armazenamento de suporte, mas você pode definir o tipo explicitamente adicionando uma anotação 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
Usando acessadores de conjunto de propriedades
Você pode definir propriedades que fornecem set
acessadores usando o <-
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)
A saída é 20.
Propriedades abstratas
As propriedades podem ser abstratas. Tal como acontece com os métodos, abstract
apenas significa que há um despacho virtual associado à propriedade. As propriedades abstratas podem ser verdadeiramente abstratas, isto é, sem uma definição na mesma classe. A classe que contém tal propriedade é, portanto, uma classe abstrata. Alternativamente, abstrato pode significar apenas que uma propriedade é virtual e, nesse caso, uma definição deve estar presente na mesma classe. Observe que as propriedades abstratas não devem ser privadas, e se um acessador é abstrato, o outro também deve ser abstrato. Para obter mais informações sobre classes abstratas, consulte Classes abstratas.
// 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