Обеспечить экземпляр класса инициализируется перед использованием


Я начинаю работу с классом модулей VBA больше в Excel и хотите, чтобы убедиться, что никакие свойства могут быть использованы, если класс не был инициализирован. Ниже находится класс и моя попытка в этом.

Private Type helper
    Prop1 As Boolean
    Prop2 As Boolean
    Prop3 As String
    Prop4 As String
End Type

Private m_initialized As Boolean

Private this As helper

Public Sub Init(ByVal Prop1 As Boolean, ByVal Prop2 As Boolean, ByVal Prop3 As String, ByVal Prop4 As String)
    this.Prop1 = Prop1
    this.Prop2 = Prop2
    this.Prop3 = Prop3
    this.Prop4 = Prop4

    m_initialized = True
End Sub

Public Property Get Prop1() As Boolean
    If Not m_initialized Then ThrowNotInitializedError

    Prop1 = this.Prop1
End Property

Public Property Get Prop2() As Boolean
    If Not m_initialized Then ThrowNotInitializedError

    Prop2 = this.Prop2
End Property

Public Property Get Prop3() As String
    If Not m_initialized Then ThrowNotInitializedError

    Prop3 = this.Prop3
End Property

Public Property Get Prop4() As String
    If Not m_initialized Then ThrowNotInitializedError

    Prop4 = this.Prop4
End Property

Public Function Self() As Foo
    Set Self = Me
End Function

Private Sub ThrowNotInitializedError()
    Err.Raise 5, "Init() not run.", "Values weren't initially assigned. Make sure they are assigned before use"
End Sub

Повторение If Not m_initialized Then ThrowNotInitializedError это часть, которая мне не нравится. Я уже представляю себя забыв поставить его на собственность. Есть ли другие варианты?

Тестирование в стандартном модуле, чтобы убедиться, что это сработало, как я ожидал

Sub TestingFooInitializeProtection()
    Dim properlyInitialized As Foo
    Set properlyInitialized = New Foo
    properlyInitialized.Init True, False, "Input3", "Input4"
    Debug.Print properlyInitialized.Prop1
    Debug.Print properlyInitialized.Prop2
    Debug.Print properlyInitialized.Prop3
    Debug.Print properlyInitialized.Prop4

    Dim improperlyInitialized As Foo
    Set improperlyInitialized = New Foo
    'improperlyInitialized.Init False, True, "input3", "input4"
    Debug.Print improperlyInitialized.Prop1 'If above line is left commented out error is thrown.
    Debug.Print improperlyInitialized.Prop2
    Debug.Print improperlyInitialized.Prop3
    Debug.Print improperlyInitialized.Prop4
End Sub


183
3
vba
задан 11 апреля 2018 в 06:04 Источник Поделиться
Комментарии
2 ответа

Несколько быстрых точек.

Если вы настраиваете класса, не инкапсулируют Type внутри него - вы не только повторять то, что класс делает! Я не уверен, где этот анти-паттерн происходит от.

В VBA не вызывать ошибки без крайней необходимости. Повышение ошибки и разведение диких приведет к непредвиденным последствиям, и вы никогда не можете быть уверены, что они будут правильно обработаны. Всегда четко обработать ошибку. Для начала, они могут нарушить глобальных переменных.

Вы можете создать Function в модуле (назовем его NewFoo) с правильной подписью - что создает Foo экземпляр и запускает Init метод. Это сделает его немного легче в остальной части вашего кода, чтобы обеспечить вам правильную последовательность.

В Excel можно воспользоваться xlError значения, чтобы вернуть ошибку, если не инициализирован. Это означает, что все ваше имущество будет вариантов (который имеет свои собственные проблемы), то вдоль линии:

Public Property Get Prop1() As Variant
Prop1 = IIF(m_initialised, CBool(Prop1_var), CVErr(xlErrNA))
End Property

Кроме того, заданы явно m_initialised в Class_Initialize обычной False. Теоретически не нужен, Но я предпочитаю быть явным.

2
ответ дан 11 апреля 2018 в 08:04 Источник Поделиться

Вы правы, VBA не допускать перегрузки инит. Однако New ключевое слово инициализирует класс. Вы можете обойти необходимость перегрузки функция такая -

Option Explicit

Private defaultFirstString As String
Private defaultSecondString As String
Private defaultThirdString As String
Private defaultFourthString As String

Private Sub Class_Initialize()
InitializeDefault
End Sub

Public Sub InitializeDefault()
defaultFirstString = "default value"
defaultSecondString = "default value"
defaultThirdString = "default value"
defaultFourthString = "default value"
End Sub

Public Sub InitializeWithValues(ByVal first As String, ByVal second As String, ByVal third As String, ByVal fourth As String)
defaultFirstString = first
defaultSecondString = second
defaultThirdString = third
defaultFourthString = fourth
End Sub

Это не здорово, но это то, что у нас есть. Оснований для применения boolean как вы не можете создать объект без инициализации. Вместо этого вы можете просто проверить значения по умолчанию, или сделать их vbNullString и тест для этого, но вне класса.

Теперь просто использовать его, как это -

Option Explicit

Public Sub main()
Dim myObject As Class1
Set myObject = New Class1

Dim myFirstObject As New Class1
myFirstObject.InitializeDefault

Dim myOtherObject As New Class1
myOtherObject.InitializeWithValues "one", "two", "three", "four"

End Sub

1
ответ дан 11 апреля 2018 в 09:04 Источник Поделиться