Структура, как и класс, представляет тип, который определяется программистом и описывает состояние и поведение объектов. В этом плане структуры походят на классы, но они более подходят для опеделения небольших объектов, которые хранят небольшие данные или имеют простое поведение.
Структура имеет два способа определения. При первом тело структуры заключается между операторами struct и end:
type [модификатор_доступа] имя_структуры = struct компоненты структуры end
При втором способе применяется атрибут [<StructAttribute>]:
[<StructAttribute>] type [модификатор_доступа] имя_структуры = компоненты структуры
Структуры, как и классы, могут иметь конструкторы, свойства, методы, поля, определяемые с помощью оператора val.
Но структуры не могут определять поля с помощью оператора let и также не могут определять выражений do, который выполняют некоторый код.
Определим простейшую структуру:
type Person = struct end // определение структуры Person let tom = Person // определение значения структуры Person
Здесь определяется структура Person и далее определяется значение этой структуры. Причем в отличие от объектов классов для создания объекта структуры нам не обязательно определять конструктор.
Если структура имеет конструктор, то для всех его параметров надо явным образом указать тип данных:
type Person(name: string, age: int) = struct end let tom = Person("Tom", 37)
В данном случае структура Person имеет конструктор с двумя параметрами, который можно использовать для создания объектов.
Как и классы, структуры могут определять дополнительные конструкторы:
type Person(name:string, age: int) = struct new(name:string) = printfn $"name: {name}" Person(name, 1) end
При определении дополнительных конструкторов они также вызывают первичный конструктор.
Альтернативное определение структуры с помощью атрибута [<StructAttribute>]:
[<Struct>] type Person(name:string, age: int) = new(name:string) = printfn $"name: {name}" Person(name, 1)
Структуры, как и классы, могут иметь методы, которые определяют поведение структуры:
type Person(name:string, age: int) = struct member _.PrintPerson() = printfn $"Name: {name} Age: {age}" end let tom = Person("Tom", 22) tom.PrintPerson() // Name: Tom Age: 22
Для хранения состояния в структрах можно использовать поля, определяемые с помощью оператора val:
type Person = struct val mutable name: string val mutable age: int end let mutable tom = Person() printfn $"Name: {tom.name} Age: {tom.age}" // Name: Age: 0 tom.name <- "Tom" // меняем значение поля name tom.age <- 23 // меняем значение поля age printfn $"Name: {tom.name} Age: {tom.age}" // Name: Tom Age: 23
Стоит отметить, что если мы хотим менять значения полей структуры, то объект структуры тоже должен быть изменяемым. Так, в примере выше значение tom
определено с ключевым словом
mutable. Консольный вывод программы:
Name: Age: 0 Name: Tom Age: 23
И в этом случае действуют те же ограничения, что и при работе с классами. Так, если структура имеет первичный конструктор, то поля должны быть изменяемыми и иметь атрибут [<DefaultValue>]
:
type Person(_name: string, _age: int) = struct [<DefaultValue>] val mutable name: string [<DefaultValue>] val mutable age: int member this.SetValues() = this.name <- _name this.age <- _age end let mutable tom = Person("Tom", 22) printfn $"Name: {tom.name} Age: {tom.age}" // Name: Age: 0 tom.SetValues() // устанавливаем начальные значения printfn $"Name: {tom.name} Age: {tom.age}" // Name: Tom Age: 22 tom.age <- 25 // меняем значение поля age printfn $"Name: {tom.name} Age: {tom.age}" // Name: Tom Age: 25
Если первичного конструктора нет, но мы хотим передать им некоторые начальные значения, то нужно определить конструктор типа, который будет инициализировать эти поля:
type Person = struct val name: string val mutable age: int new(_name:string, _age: int) = { name = _name; age=_age} end let mutable tom = Person("Tom", 22) printfn $"Name: {tom.name} Age: {tom.age}" // Name: Tom Age: 22 tom.age <- 23 // меняем значение поля age printfn $"Name: {tom.name} Age: {tom.age}" // Name: Tom Age: 23
Также структуры могут иметь свойства:
type Person(name: string) = struct member _.Name = name [<DefaultValue>] val mutable age: int member this.Age with get() = this.age and set value = this.age <- value member this.PrintPerson() = printfn $"Name: {this.Name} Age: {this.Age}" end let mutable tom = Person("Tom") tom.Age <- 24 // установка свойства tom.PrintPerson() // Name: Tom Age: 24
Здесь структура Person имеет два свойства. Первое свойство - Name
доступно только для чтения, оно получает начальное значение из первичного конструктора.
Второе свойство - Age
использует для хранения значения поле age
и доступно как для чтения, так и для записи.