屬性 (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 關鍵字。 此宣告式語法後面接著指定 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),不適用於處理自動實作屬性的初始化。 在這些情況下,請嘗試使用明確屬性。

屬性可以是類別、結構、差別聯集、記錄、介面和類型延伸模組的成員,也可以在物件運算式中定義。

屬性 (Attribute) 可以套用至屬性 (Property)。 若要將屬性 (Attribute) 套用至屬性 (Property),請在屬性 (Property) 之前於個別行上寫入屬性 (Attribute)。 如需詳細資訊,請參閱屬性

根據預設,屬性是公用的。 協助工具修飾元也可以套用至屬性。 若要套用協助工具修飾元,如果同時套用至 getset 方法,請新增至屬性名稱之前;如果各存取子需要不同協助工具,則請新增至 getset 關鍵字之前。 協助工具修飾元可以是下列其中一項: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 存取子

您可以使用 <- 運算子,設定提供 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

另請參閱