Классы F#. Свойства

Все исходники /  Язык программирования F# /  OS Windows /  Desktop /  Исходники приложений / Классы F#. Свойства

Свойства класса

Свойства синтаксически похожи на открытое поле класса, но скрывают внутри базовое значение. Свойства являются членами классов и сочетают в себе возможности полей и методов. Для хранения базового значения свойств могут использоваться закрытые поля класса, базы данных, файлы и другое. Базовое значение свойства также называют резервным хранилищем.

Свойства классов 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
Вывод результата в консольное окно:
Сегодня хорошая погода!
prop.MyProperty = 1000

Автоматические свойства

Кроме явных свойств, описанных выше, в классах 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
Вывод результата в консоль. Обратите внимание: инициализация автоматического свойства произошла однократно.

SimpleAutoProperty=637721488654682005
SimpleAutoProperty=637721488654682005

SimpleAutoProperty=637721488655128680

SimpleExplicitProperty=637721488655132293
SimpleExplicitProperty=637721488655133067

SimpleAutoProperty=17.11.2021 0:00:00

Авто-свойства класса могут применяться для информационно-вспомогательных целей. Автоматическое свойство имеет смысл применять в тех случаях, когда в классе необходимо иметь открытую переменную или открытое неизменяемое значение. Для таких случаев это рационально: авто-свойство имеет короткую запись и часть кода создаётся компилятором.

Статические свойства

В классах языка программирования 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
Вывод результата в консоль:

500
1

Исходник с примерами свойств класса F#

Примеры программного кода написаны в MS Visual Studio 2022. Примеры разделены на модули по видам свойств.

Скачать исходник