Конструкторы

В этой статье описывается, как определить и использовать конструкторы для создания и инициализации объектов класса и структуры.

Построение объектов класса

Объекты типов классов имеют конструкторы. Существует два типа конструкторов. Один из них является основным конструктором, параметры которого отображаются в скобках сразу после имени типа. Вы указываете другие дополнительные конструкторы с помощью 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

Проблемы могут возникать при попытке использовать объект до его полного определения. Поэтому использование самостоятельного идентификатора может привести к тому, что компилятор выдает предупреждение и вставляет дополнительные проверка, чтобы убедиться, что члены объекта недоступны до инициализации объекта. В привязках основного конструктора или после then ключевое слово в дополнительных конструкторах следует использовать только идентификатор do себя.

Имя идентификатора не должно быть 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 привязках в классах.

См. также