Block-Level Scope in VBA...Does Not Exist

VB.NET and VBA may look the same, but there are some important differences. This is a little-known feature of VB.NET that I really wish was available in VBA.

Block-Level Scope in VBA...Does Not Exist

One of many differences between VBA and VB.NET is that VB.NET has block-level scope and VBA does not.

A "block" is a set of statements with an opening and closing line of code, such as:

  • If ... End If
  • With ... End With
  • Select Case ... End Select
  • Do ... Loop

With block-level scope, "if you declare a variable within a block, you can use it only within that block."  

Block-Level Scope Restrictions in VB.NET

If you try to run the code below in VB.NET, you will get a bunch of compile errors as indicated in the comments and shown in the screenshot below the code:

Sub TestDim()
    Console.WriteLine(a)  '<-- Local variable 'a' cannot be referred to before it is declared.
    Dim a As Long

    If True Then
        Dim b As Long
    End If
    Console.WriteLine("b:" & b)  '<-- 'b' is not declared. It may be inaccessible due to its protection level. 

    If False Then
        Dim c As Long
    End If
    Console.WriteLine("c:" & c)  '<-- 'c' is not declared. It may be inaccessible due to its protection level. 

    With Today
        Dim d As Long
    End With
    Console.WriteLine("d:" & d)  '<-- 'd' is not declared. It may be inaccessible due to its protection level. 

    Select Case a
        Case 0
            Dim e As Long
        Case 1
            Dim f As Long
        Case Else
            Dim g As Long
    End Select
    Console.WriteLine("e:" & e)  '<-- 'e' is not declared. It may be inaccessible due to its protection level. 
    Console.WriteLine("f:" & f)  '<-- 'f' is not declared. It may be inaccessible due to its protection level. 
    Console.WriteLine("g:" & g)  '<-- 'g' is not declared. It may be inaccessible due to its protection level. 

    For a = 1 To 2
        Dim h As Long
    Next
    Console.WriteLine("h:" & h)  '<-- 'h' is not declared. It may be inaccessible due to its protection level. 

    Do Until a > 2
        Dim i As Long
        a = a + 1
    Loop
    Console.WriteLine("i:" & i)  '<-- 'i' is not declared. It may be inaccessible due to its protection level.

End Sub

No Block-Level Scope Restrictions in VBA

The nearly identical code below WILL compile in VBA.  

(I commented out the first line because that is the only one that will throw a compile error in VBA.)

Sub TestDim()
    'Debug.Print a  '<-- Compile error: Variable not defined (if uncommented)
    Dim a As Long
    
    If True Then
        Dim b As Long
    End If
    Debug.Print "b:" & b  'Available even though declared inside If ... End If block
    
    If False Then
        Dim c As Long
    End If
    Debug.Print "c:" & c   'Available even though its declaration can't be reached at runtime
    
    With CurrentProject
        Dim d As Long
    End With
    Debug.Print "d:" & d   'Available even though declared inside With ... End With block
    
    Select Case a
    Case 0
        Dim e As Long
    Case 1
        Dim f As Long
    Case Else
        Dim g As Long
    End Select
    'All three variables are available even though declared inside Select Case block
    Debug.Print "e:" & e
    Debug.Print "f:" & f
    Debug.Print "g:" & g
    
    For a = 1 To 2
        Dim h As Long
    Next
    Debug.Print "h:" & h  'Available even though declared inside For ... Next block
    
    Do Until a > 2
        Dim i As Long
        a = a + 1
    Loop
    Debug.Print "i:" & i  'Available even though declared inside Do Until ... Loop block
    
End Sub
Every Debug.Print block in the VBA sample code above works even though their associated variables were declared inside of a code block and are being referenced from outside that code block.

Only One VBA Rule - Declare Before Use

Assuming you are using Option Explicit (and you should always use Option Explicit), you only need to remember one rule when it comes to local variables:

In VBA, you must declare a local variable before you use it.

That's it.  The block-level scoping restrictions in VB.NET do not apply to VBA.  In VBA, the most restrictive level of scope is procedure-level scope.

This is a VBA Shortcoming

Why? Because it eliminates an entire class of potential compile errors.  And compile errors are good!  Okay, maybe they're not good, per se.  But they are much cheaper and easier to fix than logic errors.  

So, even though the VBA compiler won't enforce block-level scope, I still try to write my code as if it did.  In other words, if I only need to use a variable inside of a code block, then I will:

  1. Declare the variable inside the code block
  2. Not reference the variable outside the code block

While I would much rather the compiler enforce this restriction, adopting this as a policy still allows me to convey my intent for how I expect the variable to be used within the procedure.

All original code samples by Mike Wolfe are licensed under CC BY 4.0