Свойства синтаксически похожи на открытое поле класса, но скрывают внутри базовое значение. Свойства являются членами классов и сочетают в себе возможности полей и методов. Для хранения базового значения свойств могут использоваться закрытые поля класса, базы данных, файлы и другое. Базовое значение свойства также называют резервным хранилищем.
Свойства классов F# объявляются следующим синтаксисом:
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with [accessibility-modifier] get() =
get-function-body
and [accessibility-modifier] set parameter =
set-function-body
Свойства класса упрощают написание кода: визуально выражения с участием свойств выглядят краткой операцией присваивания или чтения значения. Внутри определения свойства может присутствовать программный код любой сложности для проверки и корректировки внешнего значения. Программный код свойства состоит из двух методов: метод получения базового значения get() и метод для изменения базового значения set(). Оба метода являются опциональными и свойство может быть только для чтения или только для записи.
Компилятор определяет тип свойства по типу резервного хранилища. Для случаев необходимости явного указания типа свойства тип указывается после конструкции get().
Практический пример применения свойств класса:
type ExplicitProperties() =
// Закрытая переменная для хранения значения
// свойства Simple
let mutable simple = ""
// Резервное хранилище для MyProperty
let mutable backupStorage: int = 0
// Максимально простое свойство класса.
member x.Simple
// Явное указание типа свойства.
with get (): string = simple
and set v = simple <- v
// Свойство с программным кодом внутри.
member x.MyProperty
with get () = backupStorage
and set v =
// Проверка и корректировка входного значения.
if v < 1000 then
backupStorage <- v
else
backupStorage <- 1000
[<EntryPoint>]
let main argv =
let prop = ExplicitProperties()
prop.Simple <- "Сегодня хорошая погода!"
printfn $"%s{prop.Simple}"
prop.MyProperty <- 2345
printfn $"prop.MyProperty = %d{prop.MyProperty}"
0 // return an integer exit code
Кроме явных свойств, описанных выше, в классах F# предусмотрена конструкция автоматически создаваемого свойства. Резервное хранилище для такого свойства создаёт компилятор. Для указания компилятору создать резервное хранилище служит ключевое слово val. Для объявления автоматического свойства не используется в собственный идентификатор экземпляра класса.
Синтаксис объявления автоматического свойства:
[ attributes ]
[ static ] member val [accessibility-modifier] PropertyName = initialization-expression [ with get, set ]
Автоматическое свойство может быть для чтения и записи, только для чтения, только для записи, точно также как и явное свойство. Автоматически созданное свойство является упрощенным: его методы get() и set() не могут иметь программного кода. Тело автоматического свойства может иметь только выражение инициирующее начальное значение. Инициализация происходит однократно, при создании экземпляра класса. При инициализации автоматическое свойство получает резервное хранилище.
Программный код объявления автоматических свойств класса:
type AutoProperties() =
// Автоматическое свойство инициализируется однократно
// при создании экземпляра класса.
// Инициализируется в первичном конструкторе.
// При инициализации получает автоматическое резервное хранилище.
member val SimpleAutoProperty = System.DateTime.Now with get, set
// Авто-свойство только для чтения.
//member val SimpleAutoProperty = System.DateTime.Now with get, set
// Явное свойство будет принимать новые значения при
// каждом вызове.
member this.SimpleExplicitProperty = System.DateTime.Now
// Инициализация автосвойства во
// вторичном конструкторе.
new (d: int) as con2 =
AutoProperties()
then
con2.SimpleAutoProperty <- System.DateTime.Today.AddDays(d)
[<EntryPoint>]
let main argv =
// --- Автоматические свойства ---
let prop = Properties.AutoProperties()
printfn $"SimpleAutoProperty=%s{prop.SimpleAutoProperty.Ticks.ToString()}"
printfn $"SimpleAutoProperty=%s{prop.SimpleAutoProperty.Ticks.ToString()}"
printfn $""
prop.SimpleAutoProperty <- System.DateTime.Now
printfn $"SimpleAutoProperty=%s{prop.SimpleAutoProperty.Ticks.ToString()}"
printfn $""
printfn $"SimpleExplicitProperty=%s{prop.SimpleExplicitProperty.Ticks.ToString()}"
printfn $"SimpleExplicitProperty=%s{prop.SimpleExplicitProperty.Ticks.ToString()}"
// Инициализация в дополнительном конструкторе
let prop = Properties.AutoProperties(7)
printfn $""
printfn $"SimpleAutoProperty=%s{prop.SimpleAutoProperty.ToString()}"
0 // return an integer exit code
Вывод результата в консоль. Обратите внимание: инициализация автоматического свойства произошла однократно.
Авто-свойства класса могут применяться для информационно-вспомогательных целей. Автоматическое свойство имеет смысл применять в тех случаях, когда в классе необходимо иметь открытую переменную или открытое неизменяемое значение. Для таких случаев это рационально: авто-свойство имеет короткую запись и часть кода создаётся компилятором.
В классах языка программирования F# можно определять статические свойства. Статические свойства вызываются без создания экземпляра класса и связаны непосредственно с типом класса. При определении статические свойства не требуют собственного идентификатора класса. Статическое свойство хранит значение, соответственно, в статическом базовом поле.
Статическое свойство можно определять явно или использовать автоматический способ объявления с резервным хранилищем создаваемым компилятором. Автоматически созданное статическое свойство инициируется при первом обращении к нему.
Примеры определения статических свойств в классах F#:
type StaticProperties() =
let mutable localvalue: double = 123.456
static let mutable staticvalue: double = 123.456
static let staticImmutableValue: double = 3.14
// Явное определение статического свойства.
static member StaticProperty
with get () = staticvalue
and set value =
// К локальному значению из статического свойства
// доступ невозможен.
//localvalue <- value
staticvalue <- value
// Статическое свойство только для чтения.
// Два способа объявления.
//static member StaticReadOnly with get() = staticImmutableValue
static member StaticReadOnly = staticImmutableValue
// Автоматическое статическое свойство для чтения.
// Выдаёт информацию о количествах дней в
// текущем месяце текущего года.
// Инициируется однократно при запуске приложения.
static member val StaticAutoProp =
let now = System.DateTime.Now
System.DateTime.DaysInMonth(now.Year, now.Month)
[<EntryPoint>]
let main argv =
printfn $"MyStaticProperty=%f{StaticProperties.StaticProperty}"
StaticProperties.StaticProperty <- 45.32 * 23.8745
printfn $"MyStaticProperty=%f{StaticProperties.StaticProperty}"
// Невозможно изменить свойство только для чтения.
// StaticProperties.StaticReadOnly <- 45.7
printfn $""
printfn $"%f{StaticProperties.StaticReadOnly}"
// Статическое авто-свойство для получения информации
// при запуске программы.
printfn $""
printfn $"В текущем месяце - %d{StaticProperties.StaticAutoProp} дней"
0 // return an integer exit code
Вывод результата в консоль:
MyStaticProperty=123.456000
MyStaticProperty=1081.992340
3.140000
В текущем месяце - 30 дней
Модификаторы доступа определяют область использования свойств. По умолчанию, в классах F#, свойства являются открытыми и имеют модификатор public. Доступ к таким свойствам неограничен, использовать открытые свойства можно в любых модулях и типах. Модификатор public указывать необязательно.
Для использования свойства только внутри типа используется ключевое слово private. Попытка получить доступ к закрытому свойству вне класса вызовет ошибку компиляции.
В классах F# модификаторы доступа объявляются для каждого метода свойств отдельно. Определение ключевого слова private для метода set() сформирует readonly-свойство для внешнего доступа. Модификатор доступа private перед методом get() исключит возможность чтения свойства вне класса. Если закрыть оба метода модификатором private свойство станет доступным только внутри класса.
Примеры применения модификаторов доступа:
type AccessibilityModifier(len: int) as m =
let mutable readonlyprop: int = 200
let mutable privateprop: int = 0
do m.ReadOnlyProperty <- len
// Изменение свойства можно осуществить только внутри класса.
// Для внешнего доступа это свойство только для чтения.
member x.ReadOnlyProperty
with get () = readonlyprop
and private set v =
if v < 1 then readonlyprop <- 1
else if v > 500 then readonlyprop <- 500
else readonlyprop <- v
// Свойство для внутреннего пользования классом.
member x.PrivateProperty
with private get () = privateprop
and private set (value) =
if value > 1000 then
privateprop <- 1000
else
privateprop <- value
[<EntryPoint>]
let main argv =
// --- Модификаторы доступа ---
let prop = AccessibilityModifiers.AccessibilityModifier(700)
// Нельзя изменить значение свойства вне класса.
//prop.ReadOnlyProperty <- 400
printfn $"%d{prop.ReadOnlyProperty}"
let prop = AccessibilityModifiers.AccessibilityModifier(0)
printfn $"%d{prop.ReadOnlyProperty}"
// Ошибка FS0491
// Конструктор элемента или объекта "PrivateProperty" не является accessible.
//prop.PrivateProperty <- 900
0 // return an integer exit code