屬性 (F#)
「屬性」(Property) 是代表與物件關聯之值的成員。
// 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 ]
備註
屬性表示物件導向設計程式中的 "Has a" 關聯性,代表與物件執行個體相關聯的資料 (靜態屬性則是與型別相關聯)。
您可以宣告兩種方式,取決於您是否要明確地指定屬性的基礎值 (也稱為支援存放區),或您是否願意允許編譯器會自動產生您的支援存放區的內容。 一般而言,您應該使用若屬性具有非一般的實作,更明確的方式,並自動的方法如果屬性設為只是簡單的包裝函式的值或變數。 若要明確宣告的屬性,請使用member關鍵字。 這個宣告式語法後面接著指定 get 和 set 方法 (也稱為「存取子」(Accessor)) 的語法。 各種形式的語法 」 一節中所顯示的明確語法適用於讀取/寫入、 唯讀和唯寫屬性的屬性。 對於唯讀屬性 (Property),您只需定義 get 方法;對於唯寫屬性 (Property),則只需定義 set 方法。 請注意,當屬性 (Property) 同時有 get 和 set 存取子時,替代語法可讓您對每個存取子指定不同的屬性 (Attribute) 和存取範圍修飾詞,如下列程式碼所示。
// 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
保存屬性資料的私用值稱為「備份存放區」(Backing Store)。 若要讓編譯器自動建立的支援存放區,請使用關鍵字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 方法。
警告
有某些程式庫,例如 Entity Framework (System.Data.Entity),執行自訂的作業不會自動地與元件的初始化動作的基底類別建構函式中實作的屬性。在這些情況下,請嘗試使用明確的屬性。
屬性可以是類別、結構、已區分之聯集、記錄、介面和型別擴充的成員,也可以在物件運算式中定義。
屬性 (Attribute) 可以套用至屬性 (Property), 只要在屬性 (Property) 前面的另一行撰寫屬性 (Attribute) 即可。 如需詳細資訊,請參閱屬性 (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
屬性也可以類似陣列,在此情況下稱為「索引屬性」(Indexed Property)。 如需詳細資訊,請參閱索引屬性 (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。
抽象屬性
屬性可以是抽象的。 如同方法,「抽象」(Abstract) 只表示屬性有相關聯的虛擬分派。 抽象屬性可以是真正抽象的,也就是相同類別中沒有定義。 因此,包含這類屬性的類別就是抽象類別。 此外,抽象也可以只表示屬性是虛擬的,在此情況下相同類別中必須有定義。 請注意,抽象屬性不可以是私用屬性,而且如果一個存取子是抽象存取子,另一個也必須是抽象存取子。 如需抽象類別的詳細資訊,請參閱抽象類別 (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