История вопроса
Свойства в Visual Basic позволяют инкапсулировать логику получения и задания значений. С введением свойств, код становится более читаемым и безопасным, устраняет необходимость в прямом доступе к полям класса и облегчает внедрение логики валидации или вычислений прямо в объектах.
Проблема
Новички часто делают поле публичным либо используют автосвойства без геттера/сеттера, содержащих логику, что приводит к нарушению инкапсуляции или невозможности реализовать вычисляемые значения. Еще одна проблема — рекурсивный вызов свойства внутри самого себя, приводящий к StackOverflow.
Решение
В Visual Basic объявляется приватное поле, а свойство включает блоки Get и Set с нужной логикой. Вычисляемые свойства используют только Get, возвращая вычисленное значение на основе приватных полей. Внутри set-блока всегда надо обращаться к приватному полю, чтобы избежать бесконечной рекурсии.
Пример кода:
Private _price As Decimal Private _quantity As Integer Public Property Total As Decimal Get Return _price * _quantity ' вычисляемое свойство End Get End Property Public Property Price As Decimal Get Return _price End Get Set(value As Decimal) If value < 0 Then Throw New ArgumentException("Price must be positive") _price = value End Set End Property
Ключевые особенности:
Можно ли внутри Set Price обращаться к свойству Name, если оно тоже реализовано через приватное поле?
Да, если в set-блоке свойства Price обратиться к другому свойству (например, Name), это допустимо, так как обращения к разным приватным полям не вызывают рекурсии. Надо избегать обращения к самому себе: вызов Price внутри Set Price вызовет рекурсию.
Пример кода:
Public Property Name As String Get Return _name End Get Set(value As String) _name = value End Set End Property Public Property Price As Decimal Get Return _price End Get Set(value As Decimal) If Name Is Nothing Then _name = "default" _price = value End Set End Property
Что произойдет, если внутри Get-блока свойства вызвать это свойство снова?
Это приведет к бесконечной рекурсии и StackOverflow. В get-блоке всегда используйте приватное поле, иначе свойство будет вызывать само себя.
Public Property Amount As Decimal Get Return Amount ' приведет к бесконечной рекурсии End Get Set(value As Decimal) _amount = value End Set End Property
Можно ли объявить свойство только на запись (WriteOnly) и чем это опасно?
Существуют WriteOnly свойства, но использовать их не рекомендуется, так как объект теряет возможность вернуть значение, что ухудшает читаемость и предсказуемость. Если оно нужно только для записи — лучше пересмотреть архитектуру.
Private _secret As String Public WriteOnly Property Secret As String Set(value As String) _secret = value ' Записать возможно, получить — нет. End Set End Property
Программист решил сделать поле Price публичным и напрямую работал с ним. В результате Price иногда становился отрицательным по ошибке.
Плюсы:
Минусы:
Коллега заменил Price на свойство с приватным полем и валидацией в set-блоке, что предотвратило некорректные значения.
Плюсы:
Минусы: