Propriedades (F#)
As propriedades são membros que representam os 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 ]
Comentários
As propriedades representam a relação "tem um" na programação orientada a objetos, representando os dados associados a instâncias de objeto ou, para propriedades estáticas, com o tipo.
Você pode declarar as propriedades de duas maneiras, dependendo de se você deseja especificar explicitamente o valor subjacente (também chamado de repositório de suporte) para a propriedade ou se deseja permitir que o compilador gere automaticamente o repositório de backup 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 uma variável. Para declarar uma propriedade explicitamente, use a palavra-chave member
. Essa sintaxe declarativa é seguida pela sintaxe que especifica os métodos get
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 leitura/gravação, somente leitura e somente gravação. Para propriedades somente leitura, defina apenas um método get
. Para propriedades somente gravação, defina apenas um método set
. Observe que, quando uma propriedade tem ambos os acessadores get
e set
, a sintaxe alternativa permite que você especifique atributos e modificadores de acessibilidade diferentes para cada acessador, conforme 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 ambos os métodos get
e set
, a ordem de get
e de set
pode ser invertida. Como alternativa, você pode fornecer somente a sintaxe mostrada para get
e somente a sintaxe mostrada para set
, vez de usar a sintaxe combinada. Fazer isso facilita os comentários sobre o get
individual ou o método set
, caso necessário. Essa alternativa de usar a 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 repositório de backup automaticamente, use as palavras-chave member val
, omita o autoidentificador e forneça uma expressão para inicializar a propriedade. Se a propriedade for mutável, inclua with get, set
. Por exemplo, o tipo de classe a seguir inclui duas propriedades implementadas automaticamente. Property1
é somente leitura e é inicializada para o argumento fornecido ao construtor primário e Property2
é uma propriedade configurável 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, devem ser incluídas antes de qualquer outra definição de membro, assim como as associações let
e as associações do
em uma definição de tipo. Observe que a expressão que inicializa uma propriedade implementada automaticamente é avaliada somente após a inicialização e não toda vez que a propriedade é acessada. Esse comportamento é diferente do comportamento de uma propriedade implementada explicitamente. Efetivamente, isso 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 não é alterado quando chamado repetidamente, enquanto que ExplicitProperty é alterado sempre que é chamado. Isso demonstra que a expressão de uma propriedade implementada automaticamente não é avaliada toda vez, assim como o método getter para a propriedade explícita.
Aviso
Há algumas bibliotecas, como o Entity Framework (System.Data.Entity
), que executam operações personalizadas nos construtores de classe base que não funcionam corretamente 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 nas expressões de objeto.
Os atributos podem ser aplicados a propriedades. Para aplicar um atributo a uma propriedade, grave 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 a propriedades. Para aplicar um modificador de acessibilidade, adicione-o imediatamente antes do nome da propriedade, caso seja destinado a se aplicar aos métodos get
e set
, e adicione-o antes das palavras-chave get
e set
caso uma acessibilidade diferente seja necessária para cada acessador. O modificador de acessibilidade pode ser um dos seguintes: public
, private
, internal
. Para mais informações, consulte Controle de acesso.
As implementações de propriedade são executadas sempre 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, e não com objetos individuais. Para propriedades estáticas, omita o autoidentificador. O autoidentificador é necessário para propriedades de instância.
A definição de propriedade estática a seguir é baseada em um cenário no qual você tem um campo estático myStaticValue
, que é o repositório de backup da propriedade.
static member MyStaticProperty
with get() = myStaticValue
and set(value) = myStaticValue <- value
As propriedades também podem ser semelhantes à matriz. Nesse caso, são chamadas de propriedades indexadas. Para obter mais informações, confira Propriedades Indexadas.
Anotação de Tipo para Propriedades
Em muitos casos, o compilador tem informações suficientes para inferir o tipo de uma propriedade do tipo do repositório de backup, 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
Uso de acessadores definidos pela propriedade
Você pode definir propriedades que forneçam os acessadores set
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. Assim como acontece com os métodos, abstract
significa apenas que existe uma expedição virtual associada à propriedade. As propriedades abstratas podem ser verdadeiramente abstratas, ou seja, sem uma definição na mesma classe. A classe que contém essa propriedade é, portanto, uma classe abstrata. Como alternativa, o termo abstrato pode significar apenas que uma propriedade é virtual e, nesse caso, uma definição deve ser encontrada na mesma classe. Observe que as propriedades abstratas não devem ser privadas e, se um acessador for abstrato, o outro também deverá ser abstrato. Para obter mais informações sobre classes abstratas, confira 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