Поделиться через


Конструкторы (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#)