属性 (F#)

属性 是表示与对象关联的值的成员。

语法

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

// Alternative syntax for a property that has get and set.
[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName
with [ attributes-for-get ] get() =
    get-function-body
[ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName
with [ attributes-for-set ] 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.
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with [ attributes ] get() =
    get-function-body

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

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

注解

属性表示面向对象的编程中的“具有”关系,表示与对象实例关联的数据,或者对于具有该类型的静态属性的数据。

可以通过两种方式声明属性,具体取决于是要显式指定属性的基础值(也称为后盾存储),还是希望允许编译器自动生成后盾存储。 通常,如果属性具有非简单实现,并且当属性只是值或变量的简单包装器时,应使用更明确的方法。 若要显式声明属性,请使用 member 关键字。 此声明性语法后跟指定 getset 方法(也称为 访问器)的语法。 语法节中显示的各种形式的显式语法用于读/写、只读和只读属性。 对于只读属性,仅定义一个 get 方法;对于仅写属性,仅定义一个 set 方法。 请注意,当属性同时具有 getset 访问器时,备用语法使你能够指定每个访问器不同的属性和辅助功能修饰符,如以下代码所示。

// 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

对于具有和getset方法的读/写属性,可以反转其顺序getset。 或者,可以提供仅显示的 get 语法和仅显示的 set 语法,而不是使用组合语法。 这样做可以更轻松地注释掉个人或getset方法(如果这是可能需要执行的作)。 以下代码中显示了使用此组合语法的替代方法。

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

保存属性数据的私有值称为 后盾存储。 若要让编译器自动创建后退存储区,请使用关键字 member val,省略自标识符,然后提供表达式来初始化属性。 如果属性是可变的,请包括 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.ExplicitProperty = %d{class1.ExplicitProperty}"

输出

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

上述代码的输出显示重复调用时的值 AutoProperty 保持不变,而 ExplicitProperty 每次调用时都会更改。 这演示了每次不计算自动实现属性的表达式,与显式属性的 getter 方法一样。

警告

某些库(如 Entity Framework(System.Data.Entity)在基类构造函数中执行自定义作,这些函数不适用于自动实现的属性的初始化。 在这些情况下,请尝试使用显式属性。

属性可以是类、结构、区分联合、记录、接口和类型扩展的成员,也可以在对象表达式中定义。

属性可以应用于属性。 若要将属性应用于属性,请在属性之前在单独的行上写入该属性。 有关详细信息,请参阅 属性

默认情况下,属性为公共属性。 辅助功能修饰符也可以应用于属性。 若要应用辅助功能修饰符,如果属性的名称应用于和getset方法,请立即将其添加到属性名称之前;如果每个访问器需要不同的辅助功能,请在和get关键字之前set添加它。 辅助功能修饰符可以是下列项之一:publicprivateinternal、 。 有关详细信息,请参阅 访问控制

每次访问属性时,都会执行属性实现。

静态和实例属性

属性可以是静态属性或实例属性。 可以在不使用实例的情况下调用静态属性,并用于与类型(而不是单个对象)关联的值。 对于静态属性,请省略自标识符。 实例属性需要自标识符。

以下静态属性定义基于一种场景,在该方案中,你有一个静态字段,该字段 myStaticValue 是该属性的后盾存储区。

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

属性也可以类似于数组,在这种情况下,它们称为 索引属性。 有关详细信息,请参阅 索引属性

属性的类型批注

在许多情况下,编译器有足够的信息来推断后盾存储类型的属性类型,但可以通过添加类型注释来显式设置类型。

// 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 访问器 <- 的属性。

// 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

抽象属性

属性可以是抽象的。 与方法一样, abstract 只是意味着存在与属性关联的虚拟调度。 抽象属性可以真正抽象,即在同一类中没有定义。 因此,包含此类属性的类是抽象类。 或者,抽象可能只是意味着属性是虚拟的,在这种情况下,定义必须存在于同一类中。 请注意,抽象属性不能是私有的,如果一个访问器是抽象的,另一个属性也必须是抽象的。 有关抽象类的详细信息,请参阅 抽象类

// 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

另请参阅