本文介绍如何定义和使用构造函数来创建和初始化类和结构对象。
类对象的构造
类类型的对象具有构造函数。 有两种类型的构造函数。 一个是主构造函数,其参数在类型名称之后的括号中显示。 (可选)可以使用关键字指定其他构造函数 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")
继承类中的构造函数
从具有构造函数的基类继承时,必须在继承子句中指定其参数。 有关详细信息,请参阅 构造函数和继承。
静态构造函数或类型构造函数
除了指定用于创建对象的代码外,还可以在先使用该类型在类型级别执行初始化的类类型中创作静态 let
和 do
绑定。 有关详细信息,请参阅 let
类中的绑定 和 do
类中的绑定。