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