2017-02-14 5 views
2

Я только начинаю использовать классы в VBA, и я следую методу «Наследование по конструкции», описанному here. В моем примере используется простой класс, который содержит значение (как вариант) и тип значения (в виде строки). Я создал подкласс, в котором тип значения установлен в строку в конструкторе. Вот мой код:Наследование VBA через конструкцию, конструктор не работает?

интерфейс класса (Ival)

'IVal interface class (from https://www.theartofquantfinance.com/inheritance-by-construction-in-vba/) 
Option Explicit 

'----------------------------------- 
'Accessor methods for ValType 
'----------------------------------- 
Public Property Get ValType() As String 
End Property 
Public Property Let ValType(ByVal RHS As String) 
End Property 
'----------------------------------- 
'Accessor methods for Val 
'----------------------------------- 
Public Property Get Val() As Variant 
End Property 
Public Property Let Val(ByVal RHS As Variant) 
End Property 

'Add other methods here as "Public Sub" 
'i.e. 
'Public Sub HonkHorn() 
'End Sub 

Базовый класс (CBaseVal)

'CBaseVal base class - implements IVal interface (from https://www.theartofquantfinance.com/inheritance-by-construction-in-vba/) 
Option Explicit 
Implements IVal 

'------------------------------ 
'Private Fields 
'------------------------------ 
Private valType_ As String 
Private val_ As Variant 

'------------------------------ 
'Constructors and destructors 
'------------------------------ 
Private Sub Class_Initialization() 
    valType_ = "Base Val" 
    val_ = Nothing 
End Sub 
'------------------------------ 
'Class Properties And methods - public, visible to all modules 
'These would be declared in the interface class 
'------------------------------ 
Public Property Get ValType() As String 
    ValType = valType_ 
End Property 
Public Property Let ValType(ByVal RHS As String) 
    valType_ = RHS 
End Property 

Public Property Get Val() As Variant 
    Val = val_ 
End Property 
Public Property Let Val(ByVal RHS As Variant) 
    val_ = RHS 
End Property 

'Add additional class methods here 
'e.g. 
'Public Sub HonkHorn() 
' MsgBox prompt:="Beep!!!" 
'End Sub 

'------------------------------ 
'Interface property and method implementation - private, only visible to this module 
'Delegate interface properties and methods to this (base) class using "Me" 
'------------------------------ 
Private Property Let IVal_Val(ByVal RHS As Variant) 
    Me.Val = RHS 
End Property 

Private Property Get IVal_Val() As Variant 
    IVal_Val = Me.Val 
End Property 

Private Property Let IVal_ValType(ByVal RHS As String) 
    Me.ValType = RHS 
End Property 

Private Property Get IVal_ValType() As String 
    IVal_ValType = Me.ValType 
End Property 

'End Property 
'Add additional IF methods here 
'i.e. 
'Private Sub ICar_HonkHorn() 
' Call Me.HonkHorn 
'End Sub 

Детский класс (CStringVal)

'CStringVal class - implements IVal interface and delegates to CVal as appropriate (from https://www.theartofquantfinance.com/inheritance-by-construction-in-vba/) 
Option Explicit 
Implements IVal 

'------------------------------ 
'Private Fields 
'------------------------------ 
Private baseVal_ As IVal 'contains an instance of the IVal class (why isn't this "As CBaseVal"?) 

'------------------------------ 
'Constructors and destructors 
'------------------------------ 
Private Sub Class_Initialization() 
    Set baseVal_ = New CBaseVal 'initialize private field of type "val class" 
    baseVal_.ValType = "string" 
    baseVal_.Val = Nothing 
End Sub 
'------------------------------ 
'Class Properties And methods (here's where you over-ride the base class implementation) 
'These would be declared in the base class and the interface implementation below will delegate to either here or base class 
'------------------------------ 
'e.g. 
'Public Sub HonkHorn() 
' MsgBox prompt:="Beep!!!" 
'End Sub 


'------------------------------ 
'Interface property and method implementation - declared in base class and either: 
'=> most delegate to base class 
'=> some may override base class and delegate here 
'------------------------------ 
Private Property Let IVal_Val(ByVal RHS As Variant) 
    baseVal_.Val = RHS 'Delegate to base class 
End Property 

Private Property Get IVal_Val() As Variant 
    IVal_Val = baseVal_.Val 
End Property 

Private Property Let IVal_ValType(ByVal RHS As String) 
    baseVal_.Val = RHS 'Delegate to base class 
End Property 

Private Property Get IVal_ValType() As String 
    IVal_ValType = baseVal_.ValType 'Delegate to base class 
End Property 

'Add additional interface methods here 
'i.e. 
'Private Sub ICar_HonkHorn() 
' Call Me.HonkHorn 'Overrides base class implementation, delegates to class method above 
'End Sub 

А вот код Я использую для его проверки:

Public Sub testStringValClass() 
    Dim interfaceClassVal As IVal 
    Dim baseClassVal As CBaseVal 
    Dim stringClassVal As CStringVal 

    Set interfaceClassVal = New IVal 
    Set baseClassVal = New CBaseVal 
    Set stringClassVal = New CStringVal 

    a = interfaceClassVal.ValType 
    b = baseClassVal.ValType 
    c = stringClassVal.ValType 
End Sub 

Первая проблема возникает ошибка компиляции, что «метод или член данных не найден» для линии

c = stringClassVal.ValType 

Если я закомментировать эту строку, пробеги кода, но используя часы кажется, что valType_ не является set, либо как «Base Val» для объекта базового класса, либо как «строка» для объекта класса строки. Кроме того, есть «Переменная объекта или переменная блока не установлена» ошибки для следующих свойств:

stringClassVal.IVal_Val 
stringClassVal.IVal_ValType 

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

+1

'Class_Initialize()' not 'Class_Initialization()' –

ответ

1

Это просто потому, что ваш класс CStringVal не использует свойство ValType. Хорошо, вы внедрили свойство ValType интерфейса IVal, но не явное ValType самого класса.

Для доступа к способу интерфейса вам нужно как-то до cast ваш объект к типу интерфейса. Например:

Dim itf As IVal 
Dim stringClassVal As New CStringVal 
Set itf = stringClassVal '<-- casts the object to the interface's type 
' and now: 
c = itf.ValType 

или вы можете использовать определенное имя:

c = stringClassVal.IVal_ValType 

Но обратите внимание, что вы не инициализировать поля переменной еще (используйте букву перед геттерами).

Да, наследование в VBA/VB6 как-то сложно и не очень естественно. Когда класс реализует интерфейс, методы этого интерфейса доступны через ссылку на интерфейс, а не к объекту реализатора, если последний явно не переопределяет метод/свойство

заметить также, что инициализатор val_ = Nothing бесполезно и как-то неправильно. Неинициализированный Variant создан как вариант empty. Nothing в основном используется для объектов, а не для основных переменных (включая строки).

Также как @TimWilliams сказал, что имя конструктора Class_Initialize не Class_Initialization.

+0

Позвольте мне задать пару следующих вопросов: (1) Каков наилучший способ задать дополнительные вопросы? (2) Стоит ли использовать классы, учитывая, что VBA полиморфно оспаривается? –

+0

@JamesAlesi (1) вы можете задать здесь следующие вопросы. (2) Для такого рода задач классы - это не путь, и, конечно, не интерфейсы. Классы полезны, когда у вас есть достаточно сложные объекты, которые сохраняют состояние и имеют много зависящих от состояния действий. Интерфейсы полезны для стирания стилей, то есть когда у вас разные вещи, которые выполняют один и тот же список действий, но каждый по-своему, и эти разные объекты могут находиться в каком-то контейнере. Ничего из этого не требуется в вашем предполагаемом прецеденте. Надеюсь, это уточнит :) –

+0

Следовательно, нет, классы не являются естественным выбором для программ с VBA, хотя они иногда могут оказаться полезными. –

Смежные вопросы