События

Последнее обновление: 22.08.2024

Поскольку язык F# работает на платформе .NET, то он имеет полноценную поддержку событий (events). События сигнализируют системе о том, что произошло определенное действие. И если нам надо отследить эти действия, то как раз мы можем применять события.

Например, возьмем следующий класс, который описывает банковский счет:

type Account(sum)=
    let mutable _sum = sum
    member this.Sum 
        with get() = _sum
        and private set v = _sum <- v
    member this.Put(sum) = this.Sum <- this.Sum + sum
    member this.Take(sum) = 
        if this.Sum >= sum then this.Sum <- this.Sum - sum

В конструкторе устанавливаем начальную сумму, которая хранится в свойстве Sum. С помощью метода Put мы можем добавить средства на счет, а с помощью метода Take, наоборот, снять деньги со счета. Попробуем использовать класс в программе - создать счет, положить и снять с него деньги:

let acc = Account(100)
acc.Put 20      // добавляем на счет 20
printfn "Сумма на счете: %d" acc.Sum
acc.Take 70     // пытаемся снять со счета 70
printfn "Сумма на счете: %d" acc.Sum
acc.Take 180  // пытаемся снять со счета 180
printfn "Сумма на счете: %d" acc.Sum

Консольный вывод:

Сумма на счете: 120
Сумма на счете: 50
Сумма на счете: 50

Все операции работают как и положено. Но что если мы хотим уведомлять пользователя о результатах его операций. Мы могли бы, например, для этого изменить метод Put следующим образом:

member this.Put(sum) = 
        this.Sum <- this.Sum + sum
        printfn "На счет поступило: %d" sum

Казалось, теперь мы будем извещены об операции, увидев соответствующее сообщение на консоли. Но тут есть ряд замечаний. На момент определения класса мы можем точно не знать, какое действие мы хотим произвести в методе Put в ответ на добавление денег. Это может вывод на консоль, а может быть мы захотим уведомить пользователя по email или sms. Более того мы можем создать отдельную библиотеку классов, которая будет содержать этот класс, и добавлять ее в другие проекты. И уже из этих проектов решать, какое действие должно выполняться. Возможно, мы захотим использовать класс Account в графическом приложении и выводить при добавлении на счет в графическом сообщении, а не консоль. Или нашу библиотеку классов будет использовать другой разработчик, у которого свое мнение, что именно делать при добавлении на счет. И все эти вопросы мы можем решить, используя события.

Определение и вызов событий

События объявляются в классе с помощью типа Event:

let _notify = Event<string>()

Здесь переменная _notify представляет событие. Тип Event типизируется типом, который представляет аргумент события - некоторые данные, которые мы хотим передать через событие во вне. В примере выше Event тпизирован типом string, соответственно через событие мы сможем передать строку. Но мы можем указать прочерк "_" для установки произвольного типа.

let _notify = Event<_>()

Для выова события определен метод Trigger(), в который можно передать аргумент события:

 _notify.Trigger "Произошло действие"

Поскольку событие _notify принимает один параметр типа string - строку, то при вызове события через метод Trigger нам надо передать в него строку.

Другой аспект - переменная _notify может быть неизменяемой и недоступна извне, но нам надо как-то известить код извне о том, что внутри класса произошло некоторое событие. Для этого применяется метод Publish(). И частая практика заключается в том, чтобы определить свойство, через которое внешний код может прикрепить обработчик к событию:

member this.Notify = _notify.Publish

Объединим все вместе и создадим и вызовем событие:

type Account(sum)=
    let mutable _sum = sum

    let _notify = Event<string>()      // 1.Определение события
    member this.Notify = _notify.Publish

    member this.Sum 
        with get() = _sum
        and private set v = _sum <- v
    member this.Put(sum) = 
        this.Sum <- this.Sum + sum
        _notify.Trigger $"На счет поступило: {sum}"   // 2.Вызов события 
    member this.Take(sum) = 
        if this.Sum >= sum then 
            this.Sum <- this.Sum - sum
            _notify.Trigger $"Со счета снято: {sum}"   // 2.Вызов события
        else
            _notify.Trigger $"Недостаточно денег на счете. Текущий баланс: {sum}"   // 2.Вызов события

let display message = printfn "%s" message

let acc = Account(100)
acc.Notify.Add display      // 3. Добавляем обработчик для события Notify
// можно вместо функции добавить лямбду
//acc.Notify.Add (fun msg -> display msg)

acc.Put 20
printfn "Сумма на счете: %d" acc.Sum
acc.Take 70
printfn "Сумма на счете: %d" acc.Sum
acc.Take 180  // пытаемся снять со счета 180
printfn "Сумма на счете: %d" acc.Sum

Для добавления действия, которое будет вызываться при добавлении события, применяется метод Add():

acc.Notify.Add display

Здесь в качестве обработчика события Notify применяется функция display. Она соответствует событию, так как принимает один аргумент - строку.

Теперь с помощью события Notify мы уведомляем систему о том, что были добавлены средства и о том, что средства сняты со счета или на счете недостаточно средств.

Консольный вывод программы:

На счет поступило: 20
Сумма на счете: 120
Со счета снято: 70
Сумма на счете: 50
Недостаточно денег на счете. Текущий баланс: 50
Сумма на счете: 50

Теперь мы можем выделить класс Account в отдельную библиотеку классов и добавлять в любой проект.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850