VBA – Building a C# class task with VBA

As I have already mentioned in some articles here and there, in order to find something valuable and interesting to blog about, I convert tasks from C# into VBA. The C# task from the SoftUni looked like this:


Define a class Person that has name, age and email. The name and age are mandatory. The email is optional. Define properties that accept non-empty name and age in the range [1…100]. In case of invalid argument, throw an exception. Define a property for the email that accepts either null or non-empty string containing ‘@’. Define two constructors. The first constructor should take name, age and email. The second constructor should take name and age only and call the first constructor. Implement the ToString() method to enable printing persons at the console.


 

businessPeople

Ok, writing a C# task in VBA could be somehow difficult, but I managed to do it. However, having two constructors in a class is something impossible for VBA, thus I simply decided to live with one. Furthermore, instead of overriding the  ToString() method (which is N/A) in VBA, I simply created my own function, fShowInformation. At the end, the result was not as tempting, so I made it a little fancier – I have generated three objects from class clsPerson and I have added the to a collection, assigning them random name and age. Three different e-mails were predefined to them:

    Dim colPeople       As New Collection

    Dim cNewPerson1     As New clsPerson
    Dim cNewPerson2     As New clsPerson
    Dim cNewPerson3     As New clsPerson
    
    Dim Person          As clsPerson
    Dim i               As Integer
    
    colPeople.Add cNewPerson1
    colPeople.Add cNewPerson2
    colPeople.Add cNewPerson3
    
    For Each Person In colPeople
        i = i + 1000
        Person.Name = "Peter" & CStr(i) & sSomeRandomName
        Person.Age = Int(90 * Rnd)
        Person.Email = "review" & CStr(i) & "@vitoshacademy.com"
    Next Person

Later I have decided to change one of the Persons with some normal credentials as follows:

    cNewPerson1.Name = "Vitosh"
    cNewPerson1.Age = 29
    cNewPerson1.Email = "N/A"

At the end I print the information for the Person. The trick is that if the person has no e-mail, it is written in the fShowInformation function. Later the classes are terminated.

    For Each Person In colPeople
        Debug.Print Person.fShowInformation(Person.Name, Person.Age, Person.Email)
    Next Person
    
    Set cNewPerson1 = Nothing
    Set cNewPerson2 = Nothing
    Set cNewPerson3 = Nothing

Pretty much, that is it. Here is how the thing works. If you really have the patience to wait for the 90 second demonstration, let me know 🙂

ImmediateWindowClass

Anyway, last but not least, here comes the code. Two of the functions are copy + edit + paste from the net, I would have done them differently:

Option Explicit
Public Function fValidEmail(sTestMail As String) As Boolean

'function code taken from http://www.vbaexpress.com/kb/getarticle.php?kb_id=281
'Thanks to google I do not have to write something like this.

    Dim strArray As Variant
    Dim strItem As Variant
    Dim i As Long, c As String, blnIsItValid As Boolean
    blnIsItValid = True

    i = Len(sTestMail) - Len(Application.Substitute(sTestMail, "@", ""))
    If i <> 1 Then fValidEmail = False: Exit Function
    ReDim strArray(1 To 2)
    strArray(1) = Left(sTestMail, InStr(1, sTestMail, "@", 1) - 1)
    strArray(2) = Application.Substitute(Right(sTestMail, Len(sTestMail) - Len(strArray(1))), "@", "")
    For Each strItem In strArray
        If Len(strItem) <= 0 Then
            blnIsItValid = False
            fValidEmail = blnIsItValid
            Exit Function
        End If
        For i = 1 To Len(strItem)
            c = LCase(Mid(strItem, i, 1))
            If InStr("abcdefghijklmnopqrstuvwxyz_-.", c) <= 0 And Not IsNumeric(c) Then
                blnIsItValid = False
                fValidEmail = blnIsItValid
                Exit Function
            End If
        Next i
        If Left(strItem, 1) = "." Or Right(strItem, 1) = "." Then
            blnIsItValid = False
            fValidEmail = blnIsItValid
            Exit Function
        End If
    Next strItem
    If InStr(strArray(2), ".") <= 0 Then
        blnIsItValid = False
        fValidEmail = blnIsItValid
        Exit Function
    End If
    i = Len(strArray(2)) - InStrRev(strArray(2), ".")
    If i <> 2 And i <> 3 Then
        blnIsItValid = False
        fValidEmail = blnIsItValid
        Exit Function
    End If
    If InStr(sTestMail, "..") > 0 Then
        blnIsItValid = False
        fValidEmail = blnIsItValid
        Exit Function
    End If
    fValidEmail = blnIsItValid

End Function

Public Sub Main()

    Dim colPeople       As New Collection

    Dim cNewPerson1     As New clsPerson
    Dim cNewPerson2     As New clsPerson
    Dim cNewPerson3     As New clsPerson
    
    Dim Person          As clsPerson
    Dim i               As Integer
    
    colPeople.Add cNewPerson1
    colPeople.Add cNewPerson2
    colPeople.Add cNewPerson3
    
    For Each Person In colPeople
        i = i + 1000
        Person.Name = "Peter" & CStr(i) & sSomeRandomName
        Person.Age = Int(90 * Rnd)
        Person.Email = "review" & CStr(i) & "@vitoshacademy.com"
    Next Person
    
    cNewPerson1.Name = "Vitosh"
    cNewPerson1.Age = 29
    cNewPerson1.Email = "N/A"

    For Each Person In colPeople
        Debug.Print Person.fShowInformation(Person.Name, Person.Age, Person.Email)
    Next Person
    
    Set cNewPerson1 = Nothing
    Set cNewPerson2 = Nothing
    Set cNewPerson3 = Nothing

End Sub


Function sSomeRandomName() As String
'http://stackoverflow.com/questions/22630264/ms-access-visual-basic-generate-random-string-in-text-field
    
    Dim s As String * 8 'fixed length string with 8 characters
    Dim n As Integer
    Dim ch As Integer 'the character
    For n = 1 To Len(s) 'don't hardcode the length twice
        Do
            ch = Rnd() * 127 'This could be more efficient.
            '48 is '0', 57 is '9', 65 is 'A', 90 is 'Z', 97 is 'a', 122 is 'z'.
        Loop While ch < 48 Or ch > 57 And ch < 65 Or ch > 90 And ch < 97 Or ch > 122
        Mid(s, n, 1) = Chr(ch) 'bit more efficient than concatenation
    Next

    sSomeRandomName = s

End Function

The class is named clsPerson

Option Explicit

Public sName    As String
Public iAge     As Integer
Public sEmail   As String

Public Property Get Name() As String

    Name = sName
    Debug.Print "Name taken from memory."
    
End Property

Public Property Let Name(Value As String)
    
    If Len(CStr(Value)) Then
        sName = Value
        Debug.Print "Name is assigned successfully - " & Value
    Else
        Debug.Print "The name cannot be empty!"
        sName = ""
    End If
    
End Property

Public Property Get Age() As Integer

    Age = iAge
    Debug.Print "Age taken from memory."
    
End Property

Public Property Let Age(Value As Integer)

    If (1 < Value) And (Value < 100) Then
        iAge = Value
        Debug.Print "Age is assigned successfully - " & Value
    Else
        Debug.Print "The value should be between 1 and 100!"
    End If
    
End Property

Public Property Get Email() As String

    Email = sEmail
    Debug.Print "Email taken from memory."
    
End Property

Public Property Let Email(Value As String)
    
    If fValidEmail(Value) Then
        sEmail = Value
        Debug.Print "Email is assigned successfully - " & Value
    ElseIf StrComp(Value, "N/A") = 0 Then
        sEmail = ""
    Else
        Debug.Print "Please, enter a valid e-mail!"
    End If
    
End Property

Public Function fShowInformation(fShowName As String, fShowAge As Integer, fShowMail As String) As String
           
    If Len(fShowMail) Then
        fShowInformation = fShowName & " is " & fShowAge & " years old and his e-mail address is " & fShowMail
    Else
        fShowInformation = fShowName & " is " & fShowAge & " years old and no email is available!"
    End If
    
End Function

Private Sub Class_Initialize()
    Debug.Print "ClsPerson is initialized!"
End Sub

Private Sub Class_Terminate()
    Debug.Print "ClsPerson is terminated for " & sName
End Sub

Enjoy it!