Horje

VBA - Declaring Variables
Start
  Type Hints

Type Hints are heavily discouraged. They exist and are documented here for historical and backward-compatibility reasons. You should use the As [DataType] syntax instead.

Public Sub ExampleDeclaration()

        Dim someInteger% '% Equivalent to "As Integer"
        Dim someLong&    '& Equivalent to "As Long"
        Dim someDecimal@ '@ Equivalent to "As Currency"
        Dim someSingle!  '! Equivalent to "As Single"
        Dim someDouble#  '# Equivalent to "As Double"
        Dim someString$  '$ Equivalent to "As String"

        Dim someLongLong^  '^ Equivalent to "As LongLong" in 64-bit VBA hosts
End Sub

Type hints significantly decrease code readability and encourage a legacy Hungarian Notation (opens new window) which also hinders readability:

Dim strFile$
Dim iFile%

Instead, declare variables closer to their usage and name things for what they're used, not after their type:

Dim path As String
Dim handle As Integer

Type hints can also be used on literals, to enforce a specific type. By default, a numeric literal smaller than 32,768 will be interpreted as an Integer literal, but with a type hint you can control that:

Dim foo 'implicit Variant
foo = 42& ' foo is now a Long
foo = 42# ' foo is now a Double
Debug.Print TypeName(42!) ' prints "Single"

Type hints are usually not needed on literals, because they would be assigned to a variable declared with an explicit type, or implicitly converted to the appropriate type when passed as parameters. Implicit conversions can be avoided using one of the explicit type conversion functions:

'Calls procedure DoSomething and passes a literal 42 as a Long using a type hint
DoSomething 42&

'Calls procedure DoSomething and passes a literal 42 explicitly converted to a Long
DoSomething CLng(42)

String-returning built-in functions

The majority of the built-in functions that handle strings come in two versions: A loosely typed version that returns a Variant, and a strongly typed version (ending with $) that returns a String. Unless you are assigning the return value to a Variant, you should prefer the version that returns a String - otherwise there is an implicit conversion of the return value.

Debug.Print Left(foo, 2)  'Left returns a Variant
Debug.Print Left$(foo, 2) 'Left$ returns a String

These functions are:

  • VBA.Conversion.Error -> VBA.Conversion.Error$
  • VBA.Conversion.Hex -> VBA.Conversion.Hex$
  • VBA.Conversion.Oct -> VBA.Conversion.Oct$
  • VBA.Conversion.Str -> VBA.Conversion.Str$
  • VBA.FileSystem.CurDir -> VBA.FileSystem.CurDir$
  • VBA.[_HiddenModule].Input -> VBA.[_HiddenModule].Input$
  • VBA.[_HiddenModule].InputB -> VBA.[_HiddenModule].InputB$
  • VBA.Interaction.Command -> VBA.Interaction.Command$
  • VBA.Interaction.Environ -> VBA.Interaction.Environ$
  • VBA.Strings.Chr -> VBA.Strings.Chr$
  • VBA.Strings.ChrB -> VBA.Strings.ChrB$
  • VBA.Strings.ChrW -> VBA.Strings.ChrW$
  • VBA.Strings.Format -> VBA.Strings.Format$
  • VBA.Strings.LCase -> VBA.Strings.LCase$
  • VBA.Strings.Left -> VBA.Strings.Left$
  • VBA.Strings.LeftB -> VBA.Strings.LeftB$
  • VBA.Strings.LTtrim -> VBA.Strings.LTrim$
  • VBA.Strings.Mid -> VBA.Strings.Mid$
  • VBA.Strings.MidB -> VBA.Strings.MidB$
  • VBA.Strings.Right -> VBA.Strings.Right$
  • VBA.Strings.RightB -> VBA.Strings.RightB$
  • VBA.Strings.RTrim -> VBA.Strings.RTrim$
  • VBA.Strings.Space -> VBA.Strings.Space$
  • VBA.Strings.Str -> VBA.Strings.Str$
  • VBA.Strings.String -> VBA.Strings.String$
  • VBA.Strings.Trim -> VBA.Strings.Trim$
  • VBA.Strings.UCase -> VBA.Strings.UCase$

Note that these are function aliases, not quite type hints. The Left function corresponds to the hidden B_Var_Left function, while the Left$ version corresponds to the hidden B_Str_Left function.

In very early versions of VBA the $ sign isn't an allowed character and the function name had to be enclosed in square brackets. In Word Basic, there were many, many more functions that returned strings that ended in $.

  Declaring Variables
  Constants (Const)

If you have a value that never changes in your application, you can define a named constant and use it in place of a literal value.

You can use Const only at module or procedure level. This means the declaration context for a variable must be a class, structure, module, procedure, or block, and cannot be a source file, namespace, or interface.

Public Const GLOBAL_CONSTANT As String = "Project Version #1.000.000.001"
Private Const MODULE_CONSTANT As String = "Something relevant to this Module"

Public Sub ExampleDeclaration()    

    Const SOME_CONSTANT As String = "Hello World"
    
    Const PI As Double = 3.141592653

End Sub

Whilst it can be considered good practice to specify Constant types, it isn't strictly required. Not specifying the type will still result in the correct type:

Public Const GLOBAL_CONSTANT = "Project Version #1.000.000.001" 'Still a string
Public Sub ExampleDeclaration()

    Const SOME_CONSTANT = "Hello World"           'Still a string
    Const DERIVED_CONSTANT = SOME_CONSTANT        'DERIVED_CONSTANT is also a string
    Const VAR_CONSTANT As Variant = SOME_CONSTANT 'VAR_CONSTANT is Variant/String
    
    Const PI = 3.141592653        'Still a double
    Const DERIVED_PI = PI         'DERIVED_PI is also a double
    Const VAR_PI As Variant = PI  'VAR_PI is Variant/Double
    
End Sub

Note that this is specific to Constants and in contrast to variables where not specifying the type results in a Variant type.

While it is possible to explicitly declare a constant as a String, it is not possible to declare a constant as a string using fixed-width string syntax

'This is a valid 5 character string constant
Const FOO As String = "ABCDE"

'This is not valid syntax for a 5 character string constant
Const FOO As String * 5 = "ABCDE"

  Declaring Fixed-Length Strings

In VBA, Strings can be declared with a specific length; they are automatically padded or truncated to maintain that length as declared.

Public Sub TwoTypesOfStrings()

    Dim FixedLengthString As String * 5 ' declares a string of 5 characters
    Dim NormalString As String

    Debug.Print FixedLengthString       ' Prints "     "
    Debug.Print NormalString            ' Prints ""

    FixedLengthString = "123"           ' FixedLengthString now equals "123  "
    NormalString = "456"                ' NormalString now equals "456"

    FixedLengthString = "123456"        ' FixedLengthString now equals "12345"
    NormalString = "456789"             ' NormalString now equals "456789"

End Sub

  Implicit And Explicit Declaration

If a code module does not contain Option Explicit at the top of the module, then the compiler will automatically (that is, "implicitly") create variables for you when you use them. They will default to variable type Variant.

Public Sub ExampleDeclaration()    

    someVariable = 10                  ' 
    someOtherVariable = "Hello World"
    'Both of these variables are of the Variant type.

End Sub

In the above code, if Option Explicit is specified, the code will interrupt because it is missing the required Dim statements for someVariable and someOtherVariable.

Option Explicit

Public Sub ExampleDeclaration()   

    Dim someVariable As Long 
    someVariable = 10

    Dim someOtherVariable As String
    someOtherVariable = "Hello World"

End Sub

It is considered best practice to use Option Explicit in code modules, to ensure that you declare all variables.

See VBA Best Practices (opens new window) how to set this option by default.

  Access Modifiers

The Dim statement should be reserved for local variables. At module-level, prefer explicit access modifiers:

  • Private for private fields, which can only be accessed within the module they're declared in.
  • Public for public fields and global variables, which can be accessed by any calling code.
  • Friend for variables public within the project, but inaccessible to other referencing VBA projects (relevant for add-ins)
  • Global can also be used for Public fields in standard modules, but is illegal in class modules and is obsolete anyway - prefer the Public modifier instead. This modifier isn't legal for procedures either.

Access modifiers are applicable to variables and procedures alike.

Private ModuleVariable As String
Public GlobalVariable As String

Private Sub ModuleProcedure()

    ModuleVariable = "This can only be done from within the same Module"

End Sub

Public Sub GlobalProcedure()

    GlobalVariable = "This can be done from any Module within this Project"

End Sub

Option Private Module

Public parameterless Sub procedures in standard modules are exposed as macros and can be attached to controls and keyboard shortcuts in the host document.

Conversely, public Function procedures in standard modules are exposed as user-defined functions (UDF's) in the host application.

Specifying Option Private Module at the top of a standard module prevents its members from being exposed as macros and UDF's to the host application.

  When to use a Static variable

A Static variable declared locally is not destructed and does not lose its value when the Sub procedure is exited. Subsequent calls to the procedure do not require re-initialization or assignment although you may want to 'zero' any remembered value(s).

These are particularly useful when late binding an object in a 'helper' sub that is called repeatedly.

Snippet 1: Reuse a Scripting.Dictionary object (opens new window) across many worksheets

Option Explicit

Sub main()
    Dim w As Long
    
    For w = 1 To Worksheets.Count
        processDictionary ws:=Worksheets(w)
    Next w
End Sub

Sub processDictionary(ws As Worksheet)
    Dim i As Long, rng As Range
    Static dict As Object
    
    If dict Is Nothing Then
        'initialize and set the dictionary object
        Set dict = CreateObject("Scripting.Dictionary")
        dict.CompareMode = vbTextCompare
    Else
        'remove all pre-existing dictionary entries
        ' this may or may not be desired if a single dictionary of entries
        ' from all worksheets is preferred
        dict.RemoveAll
    End If
    
    With ws
        
        'work with a fresh dictionary object for each worksheet
        ' without constructing/destructing a new object each time
        ' or do not clear the dictionary upon subsequent uses and 
        ' build a dictionary containing entries from all worksheets
    
    End With
End Sub

Snippet 2: Create a worksheet UDF that late binds the VBScript.RegExp object

Option Explicit

Function numbersOnly(str As String, _
                     Optional delim As String = ", ")
    Dim n As Long, nums() As Variant
    Static rgx As Object, cmat As Object

    'with rgx as static, it only has to be created once
    'this is beneficial when filling a long column with this UDF
    If rgx Is Nothing Then
        Set rgx = CreateObject("VBScript.RegExp")
    Else
        Set cmat = Nothing
    End If
    
    With rgx
        .Global = True
        .MultiLine = True
        .Pattern = "[0-9]{1,999}"
        If .Test(str) Then
            Set cmat = .Execute(str)
            'resize the nums array to accept the matches
            ReDim nums(cmat.Count - 1)
            'populate the nums array with the matches
            For n = LBound(nums) To UBound(nums)
                nums(n) = cmat.Item(n)
            Next n
            'convert the nums array to a delimited string
            numbersOnly = Join(nums, delim)
        Else
            numbersOnly = vbNullString
        End If
    End With
End Function

static_UDF (opens new window)
Example of UDF with Static object filled through a half-million rows

*Elapsed times to fill 500K rows with UDF:
- with **Dim rgx As Object**: 148.74 seconds
- with **Static rgx As Object**: 26.07 seconds
* These should be considered for relative comparison only. Your own results will vary according to the complexity and
    scope of the operations performed.

Remember that a UDF is not calculated once in the lifetime of a workbook. Even a non-volatile UDF will recalculate whenever the values within the range(s) it references are subject to change. Each subsequent recalculation event only increases the benefits of a statically declared variable.

  • A Static variable is available for the lifetime of the module, not the procedure or function in which it was declared and assigned.
  • Static variables can only be declared locally.
  • Static variable hold many of the same properties of a private module level variable but with a more restricted scope.

Related reference: Static (Visual Basic) (opens new window)


Read Article
https://develop.horje.com/learn/1434/reference