构造函数 (F#)

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

类对象的构造

类类型的对象有构造函数。共有两种构造函数。一种是主构造函数,其参数出现在紧靠类型名称后面的括号中。您通过使用 new 关键字来指定另一个可选的附加构造函数。任何此类附加构造函数都必须调用主构造函数。

主构造函数包含出现在类定义开头的 let 和 do。let 绑定声明类的私有字段和方法;do 绑定执行代码。有关类构造函数中的 let 绑定的更多信息,请参见类中的 let 绑定 (F#)。有关构造函数中的 do 绑定的更多信息,请参见类中的 do 绑定 (F#)

不管需要调用的构造函数是主构造函数还是附加构造函数,都能够使用 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 关键字 (F#)

执行构造函数中的副作用

类中的主构造函数可执行 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.

构造函数中的自我标识符

在其他成员中,您在每个成员的定义中为当前对象提供名称。您也可以通过使用紧跟构造函数参数的 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 绑定 (F#)类中的 do 绑定 (F#)

请参见

其他资源

成员 (F#)