属性 (F#)
“属性”是表示与对象关联的值的成员。
// 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 ]
备注
属性在面向对象的编程中代表“具有”关系,表示与对象实例关联的数据,或者,对于静态属性,则表示与类型关联的数据。
您可以声明两种方式,具体取决于是否明确指定 (也称为备份存储区) 的基础值,该属性,或如果您希望允许编译器自动生成您的备份存储的属性。通常情况下,应使用的更明确的方式,如果属性的一个重要的实现和自动的方式只是简单的包装的值或变量的属性时。若要显式地声明一个属性,请使用member关键字。此声明性语法后跟指定 get 和 set 方法(也称为“访问器”)的语法。在语法部分中所示的显式语法的各种用于读/写,只读和只写属性。对于只读属性,只定义 get 方法;对于只写属性,只定义 set 方法。请注意,当属性同时具有 get 和 set 访问器时,您可以利用可选语法指定对于每个访问器不同的特性和可访问性修饰符,如下面的代码中所示。
// 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
对于同时具有 get 和 set 方法的读/写属性,可以反转 get 和 set 的顺序。或者,您可以提供仅为 get 显示的语法和仅为 set 显示的语法,而不是使用组合语法。通过这种方法,将可以更轻松地注释掉单个 get 或 set 方法(如果可能需要这样做)。下面的代码中显示了这种使用组合语法的替代方法。
member this.MyReadWriteProperty with get () = myInternalValue
member this.MyReadWriteProperty with set (value) = myInternalValue <- value
容纳属性数据的私有值称为“后备存储”。要使编译器会自动创建备份存储区,请使用关键字, member val,省略 self-identifier,然后提供用来初始化属性。如果要将可变属性,包括with get, set。例如,下面的类类型包含两个自动实现的属性。 Property1是只读的并被初始化为主要的构造函数,提供的参数和Property2是可设置的属性初始化为空字符串:
type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set
自动实现的属性是一种类型的初始化的一部分,因此必须包含之前任何其他成员的定义,就像let绑定和do类型定义中的绑定。请注意,在初始化时并不每次访问该属性只计算进行初始化的自动实现的属性的表达式。此行为是属性的不同于显式实现的行为。这实际上意味着,用于初始化这些属性的代码添加到类的构造函数。请考虑下面的代码显示这种差异:
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
前面的代码的输出显示 AutoProperty 的值是不变时重复,调用而 ExplicitProperty 更改每次调用时。这说明自动实现的属性的表达式不计算每个时间,因为是显式的属性的 getter 方法。
警告 |
---|
有一些库,如实体框架 (System.Data.Entity) 不会自动很好地使用的初始化的基类构造函数中执行自定义操作的实现属性。在这些情况下,请尝试使用显式的属性。 |
属性可以是类、结构、可区分联合、记录、接口和类型扩展的成员,并且也可以在对象表达式中定义。
可将特性应用于属性。若要将特性应用于属性,请在属性前面另起一行编写特性。有关更多信息,请参见特性 (F#)。
默认情况下,属性是公共属性。也可以将可访问性修饰符应用于属性。若要应用可访问性修饰符,如果要将属性同时应用于 get 和 set 方法,请将其添加在紧靠属性名称的前面;如果每个访问器需要不同的可访问性,则将其添加在 get 和 set 关键字前面。accessibility-modifier 可以是以下值之一:public、private 和 internal。有关更多信息,请参见访问控制 (F#)。
每次访问一个属性时都会执行属性实现。
静态属性和实例属性
属性可以是静态属性或实例属性。静态属性可以在没有实例的情况下调用,并且用于与类型(而不是个别对象)关联的值。对于静态属性,请省略 self-identifier。Self-identifier 不需要实例属性。
以下静态属性定义基于这样一种方案:您有一个用作属性后备存储的静态字段 myStaticValue。
static member MyStaticProperty
with get() = myStaticValue
and set(value) = myStaticValue <- value
属性也可以像数组一样,在这种情况下,属性称为“索引属性”。有关更多信息,请参见索引属性 (F#)。
属性的类型批注
多数情况下,编译器都有足够的信息,可通过后备存储的类型推断出属性的类型,但您可以通过添加类型批注显式地设置类型。
// 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
使用属性 set 访问器
可通过使用 <- 运算符来设置提供 set 访问器的属性。
// 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)
输出为 20。
抽象属性
属性可以是抽象的。与方法一样,“抽象”只是意味着存在与属性关联的虚拟调度。抽象属性可以是真正抽象的,也就是说,在同一个类中没有定义。包含此类属性的类因而也就成为了抽象类。或者,“抽象”可能只是意味着属性是虚拟的,在这种情况下,同一个类中必须存在定义。请注意,抽象属性不能为私有属性,并且,如果一个访问器是抽象的,则另一个访问器也必须是抽象的。有关抽象类的更多信息,请参见抽象类 (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