建構函式
本文描述如何定義和使用建構函式,以建立和初始化類別和結構物件。
建構類別物件
類別類型的物件具有建構函式。 有兩種建構函式。 一種是主要建構函式,其參數會出現在類型名稱正後方的括弧中。 您也可以使用 new
關鍵字來指定其他選擇性建構函式。 任何這類其他建構函式都必須呼叫主要建構函式。
主要建構函式包含出現在類別定義開頭的 let
和 do
繫結。 let
繫結會宣告類別的私用欄位和方法;do
繫結會執行程式碼。 如需類別建構函式中 let
繫結的詳細資訊,請參閱類別中的 let
繫結。 如需建構函式中 do
繫結的詳細資訊,請參閱類別中的 do
繫結。
無論您要呼叫的建構函式是主要建構函式還是其他建構函式,都可以使用 new
運算式搭配或不搭配選擇性 new
關鍵字來建立物件。 您可以將物件與建構函式引數一起初始化,方法是依序列出引數,並以逗號分隔及以括弧括住,或是使用括弧中的具名引數和值。 您也可以使用屬性名稱並指派值,在建構物件期間設定物件的屬性,就像是使用具名建構函式引數一樣。
下列程式碼說明具有建構函式的類別,以及建立物件的各種方式:
// This class has a primary constructor that takes three arguments
// and an additional constructor that calls the primary constructor.
type MyClass(x0, y0, z0) =
let mutable x = x0
let mutable y = y0
let mutable z = z0
do
printfn "Initialized object that has coordinates (%d, %d, %d)" x y z
member this.X with get() = x and set(value) = x <- value
member this.Y with get() = y and set(value) = y <- value
member this.Z with get() = z and set(value) = z <- value
new() = MyClass(0, 0, 0)
// Create by using the new keyword.
let myObject1 = new MyClass(1, 2, 3)
// Create without using the new keyword.
let myObject2 = MyClass(4, 5, 6)
// Create by using named arguments.
let myObject3 = MyClass(x0 = 7, y0 = 8, z0 = 9)
// Create by using the additional constructor.
let myObject4 = MyClass()
輸出如下所示:
Initialized object that has coordinates (1, 2, 3)
Initialized object that has coordinates (4, 5, 6)
Initialized object that has coordinates (7, 8, 9)
Initialized object that has coordinates (0, 0, 0)
建構結構
結構會遵循類別的所有規則。 因此,您可以有主要建構函式,也可以使用 new
來提供其他建構函式。 不過,結構與類別之間有一個重大差異:結構可以有無參數的建構函式 (即不含引數的建構函式),即使未定義主要建構函式也一樣。 無參數的建構函式會將所有欄位初始化為該類型的預設值,通常是零或其對等項。 您為結構定義的任何建構函式必須至少有一個引數,才不會與無參數的建構函式衝突。
此外,結構通常會有使用 val
關鍵字建立的欄位;類別也可以有這些欄位。 具有使用 val
關鍵字定義之欄位的結構和類別也可以使用記錄運算式在其他建構函式中初始化,如下列程式碼所示。
type MyStruct =
struct
val X : int
val Y : int
val Z : int
new(x, y, z) = { X = x; Y = y; Z = z }
end
let myStructure1 = new MyStruct(1, 2, 3)
如需詳細資訊,請參閱明確欄位:val
關鍵字。
在建構函式中執行副作用
類別中的主要建構函式可以執行 do
繫結中的程式碼。 不過,如果您必須在沒有 do
繫結的其他建構函式中執行程式碼,該怎麼辦? 若要執行此作業,您可以使用 then
關鍵字。
// Executing side effects in the primary constructor and
// additional constructors.
type Person(nameIn : string, idIn : int) =
let mutable name = nameIn
let mutable id = idIn
do printfn "Created a person object."
member this.Name with get() = name and set(v) = name <- v
member this.ID with get() = id and set(v) = id <- v
new() =
Person("Invalid Name", -1)
then
printfn "Created an invalid person object."
let person1 = new Person("Humberto Acevedo", 123458734)
let person2 = new Person()
主要建構函式的副作用仍會執行。 因此,其輸出如下:
Created a person object.
Created a person object.
Created an invalid person object.
需要 then
而不是另一個 do
的原因是,當 do
關鍵字存在於其他建構函式主體時,具有分隔 unit
傳回運算式的標準意義。 只有在主要建構函式內容中才有特殊意義。
建構函式中的自我識別碼
在其他成員中,您會在每個成員的定義中提供目前物件的名稱。 您也可以緊接在建構函式參數後面使用 as
關鍵字,將自我識別碼放在類別定義的第一行。 下列範例說明此語法。
type MyClass1(x) as this =
// This use of the self identifier produces a warning - avoid.
let x1 = this.X
// This use of the self identifier is acceptable.
do printfn "Initializing object with X =%d" this.X
member this.X = x
在其他建構函式中,您也可以將 as
子句放在建構函式參數的正後方來定義自我識別碼。 下列範例說明此語法:
type MyClass2(x : int) =
member this.X = x
new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X
當您嘗試在物件完全定義之前使用物件時,可能會發生問題。 因此,使用自我識別碼可能會導致編譯器發出警告,並插入其他檢查,以確保在初始化物件之前不會存取物件的成員。 您只能在主要建構函式的 do
繫結中,或在其他建構函式的 then
關鍵字後面使用自我識別碼。
自我識別碼的名稱不一定必須為 this
。 它可以是任何有效的識別碼。
在初始化時將值指派給屬性
您可以在初始化程式碼中將值指派給類別物件的屬性,方法是將 property = value
形式的指派清單附加至建構函式的引數清單。 這會顯示在以下程式碼範例中:
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
上述程式碼的下列版本說明如何將一般引數、選擇性引數和屬性設定組合成一個建構函式呼叫:
type Account(accountNumber : int, ?first: string, ?last: string, ?bal : float) =
let mutable balance = defaultArg bal 0.0
let mutable number = accountNumber
let mutable firstName = defaultArg first ""
let mutable lastName = defaultArg last ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(8782108, bal = 543.33,
FirstName="Raman", LastName="Iyer")
繼承類別中的建構函式
從具有建構函式的基底類別繼承時,您必須在 inherit 子句中指定其引數。 如需詳細資訊,請參閱建構函式和繼承。
靜態建構函式或類型建構函式
除了指定建立物件的程式碼之外,還可在第一次使用類型之前執行的類別類型中撰寫靜態 let
和 do
繫結,以便在類型層級執行初始化。 如需詳細資訊,請參閱類別中的 let
繫結和類別中的 do
繫結。