I'm studying this simple macro but I don't understand why it isn't necessary to set the object variable named ws
with an object reference before using the For Each ... Next
loop. My logic is:
Dim ws As worksheet
simply creates the memory space to hold a worksheet
object reference. So as far I am concerned, it's an empty object variable. It doesn't hold an object reference yet. Only its "datatype" has been specified.
So when we reference ws
in the line For each ws In ActiveWorkbook.Worksheets
, isn't the ws
variable technically empty???? Shouldn't there be some line where we take ws = ActiveSheet
so that the variable actually contains an object reference to a worksheet? So confused.
Sub FormatFormulas()
Dim ws As worksheet
On Error Resume Next
For Each ws In ActiveWorkbook.Worksheets
With ws.Cells.SpecialCells(xlCellTypeFormulas)
.NumberFormat = ”#,##0”
.Interior.ColorIndex = 36
.Font.Bold = True
End With
Next ws
End Sub
The run-time behavior in For Each
loops is spelled out in section 5.4.2.4 of the VBA language specification.
For arrays:
- If the declared type of the array is Object, then the <bound-variable-expression> is Set-assigned to the first element in the array. Otherwise, the <bound-variable-expression> is Let-assigned to the first element in the array.
- After <bound-variable-expression> has been set, the <statement-block> is executed. If a <nested-for-statement> is present, it is then executed.
- Once the <statement-block> and, if present, the <nested-for-statement> have completed execution, <bound-variable-expression> is Let-assigned to the next element in the array (or Set-assigned if it is an array of Object). If and only if there are no more elements in the array, then execution of the <for-each-statement> immediately completes. Otherwise, <statement-block> is executed again, followed by <nested-forstatement> if present, and this step is repeated.
For other enumerable types (in your case Workbook.Worksheets
), this is the run-time behavior:
- The data value of <collection> must be an object-reference to an external object that supports an implementation-defined enumeration interface. The <bound-variable-expression> is either Let-assigned or Set-assigned to the first element in <collection> in an implementation-defined manner.
- After <bound-variable-expression> has been set, the <statement-block> is executed. If a <nested-for-statement> is present, it is then executed.
- Once the <statement-block> and, if present, the <nested-for-statement> have completed execution, <bound-variable-expression> is Set-assigned to the next element in <collection> in an implementation-defined manner. If there are no more elements in <collection>, then execution of the <for-each-statement> immediately completes. Otherwise, <statement-block> is executed again, followed by <nested-for-statement> if present, and this step is repeated.
In your case, what this basically boils down to is that there is an implicit call generated by the compiler that performs the following assignment when entering the loop for the first time and for each iteration. It would be something similar to this once it's compiled (in reality it's a bit more involved than a check for Nothing
):
Set ws = ActiveWorkbook.Worksheets.[_NewEnum]
Do While ws Is Not Nothing
With ws.Cells.SpecialCells(xlCellTypeFormulas)
.NumberFormat = "#,##0"
.Interior.ColorIndex = 36
.Font.Bold = True
End With
Set ws = ActiveWorkbook.Worksheets.[_NewEnum]
Loop
Note that this is why you shouldn't alter the contents of a collection while you're iterating over it- the next element is retrieved by calling [_NewEnum]
. This means that the items returned by the underlying collection are "yielded" one at a time, so altering the contents of the collection inside the loop will either effect which items are returned or could potentially cause an error (the behavior of [_NewEnum]
is implementation specific).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With