Для создания объекта класса необходим вызов конструктора. Есть два типа конструкторов: первичные или основные и вторичные или дополнительные.
Каждый класс имеет один первичный конструктор. Как и обычная функция, конструктор может иметь параметры. Например:
type Person() = do printfn "Создание объекта класса Person" let tom = Person()
Перечисление параметров идет в скобках после имени класса. В примере выше параметры не используются, поэтому после имени класса указаны пустые скобки. Теперь определим пару параметров:
type Person (name, age) = do printfn $"Person name: {name} age: {age}" let tom = Person("Tom", 37) let bob = Person("Bob", 41)
Здесь первичный конструктор имеет два параметра: name и age. Параметры определяются в скобках через запятую. При вызове конструктора мы должны передать ему значения для его параметров.
Стоит отметить, что в определении класса после знака равно (=) фактически идут действия, выполняемые первичным конструктором. Поэтому мы можем обратиться к значениям параметров:
do printfn $"Person name: {name} age: {age}"
Результат работы программы:
Person name: Tom age: 37 Person name: Bob age: 41
При этом параметры конструктора также можно типизировать, а при вызове конструктора передавать параметрам значения по имени:
type Person (name: string, age: int) = do printfn $"Person name: {name} age: {age}" let tom = Person("Tom", 37) // передача значений параметрам по позиции let sam = Person(age = 24, name= "Sam") // передача значений параметрам по имени
Класс может иметь только один первичный конструктор. Но в дополнение к нему можно определить несколько вторичных конструкторов. Они определяются с помощью ключевого слова new. Вторичный конструктор должен вызывать первичный. Например, определим вторичный конструктор в классе Person:
type Person (name, age) = do printfn $"Person name: {name} age: {age}" new() = Person("Undefined", 1) let tom = Person("Tom", 37) let bob = Person()
Строка
new() = Person("Undefined", 1)
представляет определение вторичного конструктора - оно начинается с ключевого слова new, после которого в скобках идут параметры этого конструктора. В данном случае вторичный конструктор не имеет параметров. После знака равно идет вызов первичного конструктора. Вызов первичного конструктора представляет имя класса, после которого в скобках передаются значения для параметров первичного конструктора. То есть фактически также, как мы создаем объект класса.
Поскольку мы определили второй конструктор, мы можем его использовать для создания объекта класса Person:
let bob = Person()
В данном случае будет выполняться вызов Person("Undefined", 1)
.
Консольный вывод программы:
Person name: Tom age: 37 Person name: Undefined age: 1
Вторичных конструкторов может быть множество. Так, добавим еще один вторичный конструктор:
type Person (name, age) = do printfn $"Person name: {name} age: {age}" new() = Person("Undefined", 1) new(name) = Person(name, 1) let tom = Person("Tom", 37) // вызов первичного конструктора let bob = Person() // вызов первого вторичного конструктора let sam = Person("Sam") // вызов второго вторичного конструктора
Здесь добавлен второй вторичный конструктор, который принимает один параметр:
new(name) = Person(name, 1)
Он также вызывает первичный конструктор, только его первому параметру передает значение своего единственного параметра.
После этого мы также сможем использовать этот конструктор для создания объектов:
let sam = Person("Sam")
Консольный вывод программы:
Person name: Tom age: 37 Person name: Undefined age: 1 Person name: Sam age: 1
Выше в примере оба вторичных конструктора вызывали первичный, однако они также могут вызывать и один из вторичных конструкторов, главное, чтобы цепочка вызовов конструкторов в конечном счете привела к вызову первичного конструктора:
type Person (name, age) = do printfn $"Person name: {name} age: {age}" new(name) = Person(name, 1) // вызываем первичный конструктор new() = Person("Undefined") // вызываем первый вторичный конструктор
Таким образом, создания объекта:
let bob = Person()
Приведет к вызову вторичного конструктора
new() = Person("Undefined")
Который вызовет другой вторичный конструктор:
new(name) = Person(name, 1)
А этот вызов, в свою очередь, приведет к вызову первичного конструктора.
Выше мы увидели, как вызывать некоторые действия в первичном конструкторе (с помощью выражения do), но что, если мы хотим также определить некоторые действия во вторичном конструкторе? В этом случае используется выражение then:
type Person (name, age) = do printfn $"Person name: {name} age: {age}" new(name) = // определение вторичного конструтора Person(name, 1) then // действия выполняемые во вторичном конструкторе printfn $"Для name передано значение: {name}" printfn $"Для age будет передано значение: 1" new() = Person("Undefined") let sam = Person("Sam") // вызов второго вторичного конструктора
Теперь первый вторичный конструктор выглядит следующим образом:
new(name) = // определение вторичного конструтора Person(name, 1) then // действия выполняемые во вторичном конструкторе printfn $"Для name передано значение: {name}" printfn $"Для age будет передано значение: 1"
После оператора then помещаются все дополнительные действия, выполняемые этим конструктором.
Консольный вывод программы:
Person name: Sam age: 1 Для name передано значение: Sam Для age будет передано значение: 1