Igor Cheglakov
Igor Cheglakov

Reputation: 555

Cannot make complex class

I am trying to make a complex class with following structure:

Obj {
    name {
        value = ""
        tag = "name"
    }
    id {
        value = ""
        tag = "id"
    }
    address {
        value = ""
        tag = "address"
    }
}

First I created a RefPair class that looks like this:

Public tag As String
Public value As String

Then a LawSubject class that looks like this:

Public name As New RefPair
    name.tag = "name"

Public id As New RefPair
    id.tag = "id"

Public address As New RefPair
    address.tag = "address"

When I try to call this class I get an error on name.tag = "name" string: compile error: invalid outside procedure What am I getting wrong?

Upvotes: 0

Views: 75

Answers (3)

ARickman
ARickman

Reputation: 601

What you need is a Parent object LawSubject to hold references to a child object RefPair. The implementation closest to your example in (which I think is C++ ?) would be the following:

Class: RefPair

Option Explicit

Private Type TRefPair
    Tag As String
    Value As String
End Type

Private this As TRefPair

Public Property Let Tag(ByVal Value As String)
    this.Tag = Value
End Property

Public Property Get Tag() As String
    Tag = this.Tag
End Property


Public Property Let Value(ByVal Value As String)
    this.Value = Value
End Property

Public Property Get Value() As String
    Value = this.Value
End Property

Class: LawSubject

Option Explicit

Private Type TLawSubject
    Name As New RefPair
    Id As New RefPair
    Address As New RefPair
End Type

Private this As TLawSubject

Public Property Get Name() As RefPair
    Set Name = this.Name
End Property

Public Property Get Id() As RefPair
    Set Id = this.Id
End Property

Public Property Get Address() As RefPair
    Set Address = this.Address
End Property

Usage:

Option Explicit

Sub Testing()

    Dim LwSbjct As LawSubject

    Set LwSbjct = New LawSubject

        LwSbjct.Address.Tag = "Address Tag"
        LwSbjct.Address.Value = "Address Value"

        LwSbjct.Name.Tag = "Name Tag"
        LwSbjct.Name.Value = "Name Value"

        LwSbjct.Id.Tag = "Id Tag"
        LwSbjct.Id.Value = "Id Value"


        Debug.Print LwSbjct.Address.Tag
        Debug.Print LwSbjct.Address.Value

        Debug.Print LwSbjct.Name.Tag
        Debug.Print LwSbjct.Name.Value

        Debug.Print LwSbjct.Id.Tag
        Debug.Print LwSbjct.Id.Value

End Sub

Immediate Window Results:

enter image description here

Upvotes: 0

Nathan_Sav
Nathan_Sav

Reputation: 8531

There is also a way using a dictionary, maybe overkill, but gives the ability to add new data pairs on the fly.

A class as so

Public Extract As Scripting.Dictionary

Private Sub Class_Initialize()

Set Extract = New Scripting.Dictionary

AddDefault "Name", "1"
AddDefault "ID", "2"
AddDefault "Address", "3"

End Sub

Private Sub Class_Terminate()
    Set Extract = Nothing
End Sub

Public Function AddToExisting(strKey As String, _
                            strContentsKey As String, _
                            strContentsValue As String)

        Extract(strKey).Add strContentsKey, strContentsValue

End Function

Private Function AddDefault(strKey As String, strValue As String)
    Extract.Add strKey, CreateKeyValuePair(strKey & " Tag", strValue)
End Function

Private Function CreateKeyValuePair( _
                                        strKey As String, _
                                        strValue As String) As Scripting.Dictionary

Dim dicTemp As New Scripting.Dictionary

With dicTemp
    .Add "Tag", strKey
    .Add "Value", strValue
End With

Set CreateKeyValuePair = dicTemp

End Function

Then used like so

Sub testComplex()

Dim c As New clsComplex

Debug.Print c.Extract("Name")("Tag")
Debug.Print c.Extract("Name")("Value")

c.AddToExisting "ID", "Date of Value", CStr(Date)
c.AddToExisting "ID", "Date of Value Check", CStr(Date + 20)

Debug.Print c.Extract("ID")("Tag")
Debug.Print c.Extract("ID")("Value")
Debug.Print c.Extract("ID")("Date of Value")
Debug.Print c.Extract("ID")("Date of Value Check")

c.AddToExisting "Address", "Address Proof", "Utility Bill"
Debug.Print c.Extract("Address")("Tag")
Debug.Print c.Extract("Address")("Value")
Debug.Print c.Extract("Address")("Address Proof")

Set c = Nothing

End Sub

Upvotes: 0

Mrblackey
Mrblackey

Reputation: 94

You are trying to set an object's property, which in VBA is only possible inside a procedure. (Sub, Function, or Property)

In alternative, you can move your property let statements to the Class_Initialize procedure:

Public name As New RefPair
Public id As New RefPair
Public address As New RefPair

Private Sub Class_Initialize()
    name.tag = "name"
    id.tag = "id"
    address.tag = "address"
End Sub

But may I suggest you shouldn't be using a class module for storing 2 string values? The overhead is enormous. Instead, consider using Type:

Private Type RefPairType
  tag As String
  value As String
End Type

Private Name As RefPairType
Private Id As RefPairType
Private Address As RefPairType

Private Sub Class_Initialize()
    Name.tag = "name"
    Id.tag = "id"
    Address.tag = "address"
End Sub

This way, you can encapsulate the relevant data for your class, while achieving better control over the process of manipulating the internal values from the outside, by providing property accessors. For example:

Private Type RefPairType
    tag As String
    value As String
End Type

Private Type LawSubjectType
    Name As RefPairType
    Id As RefPairType
    Address As RefPairType
End Type

Private This As LawSubjectType

Private Sub Class_Initialize()
    With This
        .Name.tag = "name"
        .Id.tag = "id"
        .Address.tag = "address"
    End With
End Sub

' Allow outside code to get the value of the Name pair
Public Property Get NameValue() As String
    NameValue = This.Name.value
End Property

' Allow outside code to get the value of the ID pair
Public Property Get IdValue() As String
    IdValue = This.Id.value
End Property

' Allow outside code to get and modify the value of the Address pair
Public Property Get AddressValue() As String
    AddressValue = This.Address.value
End Property

Public Property Let AddressValue(val As String)
    This.Address.value = val
End Property

Upvotes: 4

Related Questions