构造函数

本文介绍如何定义和使用构造函数来创建和初始化类和结构对象。

类对象的构造

类类型的对象具有构造函数。 有两种构造函数。 其中一个是主构造函数,其参数显示在紧随类型名称之后的括号中。 可以使用 new 关键字指定其他可选附加构造函数。 任何此类附加构造函数都必须调用主构造函数。

主构造函数包含出现在类定义开头的 letdo 绑定。 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 子句中指定其参数。 有关详细信息,请参阅构造函数和继承

静态构造函数或类型构造函数

除了指定用于创建对象的代码之外,还可以在类类型中编写静态 letdo 绑定,这些类型在首次用于在类型级别执行初始化之前执行。 有关详细信息,请参阅类中的 let 绑定类中的 do 绑定

另请参阅