明確欄位:val 關鍵字
val
關鍵字用來宣告位置以儲存類別中或結構類型中的值,但不初始化。 以這種方式宣告的儲存位置稱為明確欄位。 關鍵詞的另 val
一個用法是搭配 member
關鍵詞來宣告自動實作的屬性。 如需自動實作屬性的詳細資訊,請參閱 屬性。
語法
val [ mutable ] [ access-modifier ] field-name : type-name
備註
在類別或結構類型中定義欄位通常是使用 let
繫結。 不過,let
繫結必須在類別建構函式中初始化,但不一定總是可行、必要或適合。 當您想要未初始化的欄位時,您可以使用 val
關鍵字。
明確欄位可以是靜態或非靜態。 access-modifier 可以是 public
、private
或 internal
。 根據預設,明確欄位是 public。 這不同於類別中永遠是 private 的 let
繫結。
在具有主要建構函式的類別型別中,明確欄位需要 DefaultValue 屬性。 這個屬性指定欄位初始化為零。 欄位的類型必須支援零初始化。 下列類型支援零初始化:
- 具有零值的基本類型。
- 支援 null 值的類型,可能為正常值、異常值或值的一種表示法。 這包括類別、Tuple、記錄、函式、介面、.NET 參考類型、
unit
類型,以及差別聯集類型。 - .NET 實值類型。
- 所有欄位都支援預設零值的一種結構。
例如,稱為 someField
的不可變動欄位在 .NET 編譯表示法中有一個名稱為 someField@
的支援欄位,您可以使用名為 someField
的屬性來存取儲存的值。
對於可變動欄位,.NET 編譯表示法是 .NET 欄位。
警告
.NET Framework 命名空間 System.ComponentModel
包含具有相同名稱的屬性。 如需此屬性的詳細資訊,請參閱 DefaultValueAttribute。
下列程式碼示範在具有主要建構函式的類別中使用明確欄位,也示範 let
繫結,方便對照。 請注意,let
繫結的欄位 myInt1
是 private。 從成員方法參考 let
繫結欄位 myInt1
時,不需要自我識別項 this
。 但是,當您參考明確欄位 myInt2
和 myString
時,需要自我識別項。
type MyType() =
let mutable myInt1 = 10
[<DefaultValue>] val mutable myInt2 : int
[<DefaultValue>] val mutable myString : string
member this.SetValsAndPrint( i: int, str: string) =
myInt1 <- i
this.myInt2 <- i + 1
this.myString <- str
printfn "%d %d %s" myInt1 (this.myInt2) (this.myString)
let myObject = new MyType()
myObject.SetValsAndPrint(11, "abc")
// The following line is not allowed because let bindings are private.
// myObject.myInt1 <- 20
myObject.myInt2 <- 30
myObject.myString <- "def"
printfn "%d %s" (myObject.myInt2) (myObject.myString)
輸出如下所示:
11 12 abc
30 def
下列程式碼示範在沒有主要建構函式的類別中使用明確欄位。 在此例子中,DefaultValue
屬性不是必要的,但在類型所定義的建構函式中必須初始化所有欄位。
type MyClass =
val a : int
val b : int
// The following version of the constructor is an error
// because b is not initialized.
// new (a0, b0) = { a = a0; }
// The following version is acceptable because all fields are initialized.
new(a0, b0) = { a = a0; b = b0; }
let myClassObj = new MyClass(35, 22)
printfn "%d %d" (myClassObj.a) (myClassObj.b)
輸出為 35 22
。
下列程式碼示範在結構中使用明確欄位。 由於結構是實值型別,因此會自動有無參數建構函式將其欄位的值設定為零。 因此,不需要 DefaultValue
屬性。
type MyStruct =
struct
val mutable myInt : int
val mutable myString : string
end
let mutable myStructObj = new MyStruct()
myStructObj.myInt <- 11
myStructObj.myString <- "xyz"
printfn "%d %s" (myStructObj.myInt) (myStructObj.myString)
輸出為 11 xyz
。
請注意,如果您要使用不含 mutable
關鍵字的 mutable
欄位來初始化結構,您的指派將會作用於會在指派之後隨即捨棄的結構複本。 因此,您的結構不會變更。
[<Struct>]
type Foo =
val mutable bar: string
member self.ChangeBar bar = self.bar <- bar
new (bar) = {bar = bar}
let foo = Foo "1"
foo.ChangeBar "2" //make implicit copy of Foo, changes the copy, discards the copy, foo remains unchanged
printfn "%s" foo.bar //prints 1
let mutable foo' = Foo "1"
foo'.ChangeBar "2" //changes foo'
printfn "%s" foo'.bar //prints 2
平常不適合使用明確欄位。 一般而言,可能的話,應該在類別中使用 let
繫結,而不是明確欄位。 在某些互通性案例中,例如您需要定義結構供平台叫用呼叫原生 API,或在 COM interop 案例中,明確欄位很有用。 如需詳細資訊,請參閱外部函式。 另一種情況是您使用 F# 程式碼產生器來產生沒有主要建構函式的類別,這時也可能需要明確欄位。 對於執行緒靜態變數或類似的建構,明確欄位也很有用。 如需詳細資訊,請參閱System.ThreadStaticAttribute
。
當關鍵字 member val
一起出現在類型定義中時,這是自動實作屬性的定義。 如需詳細資訊,請參閱 屬性中定義的介面的私用 C++ 專屬實作。