我正在研究这个简单的宏,但是我不明白为什么在使用ws
循环之前不必使用对象引用设置名为For Each ... Next
的对象变量。我的逻辑是:
Dim ws As worksheet
仅创建存储空间来保存worksheet
对象引用。就我而言,这是一个空的对象变量。它还没有保存对象引用。仅指定了其“数据类型”。
因此,当我们在ws
行中引用For each ws In ActiveWorkbook.Worksheets
时,ws
变量在技术上是否为空?我们应该在ws = ActiveSheet
上使用一行,以便该变量实际上包含对工作表的对象引用吗?太困惑了。
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
答案 0 :(得分:6)
For Each
循环中的运行时行为在VBA语言规范的section 5.4.2.4中有详细说明。
对于数组:
- 如果数组的声明类型为 Object ,则
被 Set 分配给第一个元素 在数组中。否则, 为 让我们分配给数组中的第一个元素。 - 设置
后,将执行 。如果存在 ,则将其执行。 - 一旦
和 完成(如果存在),就将 分配给数组中的下一个元素(或Set-assigned (如果它是Object的数组)。当且仅当数组中没有其他元素时, 的执行立即完成。否则,将再次执行 ,然后执行 (如果存在),然后重复此步骤。
对于其他可枚举类型(在您的情况下为Workbook.Worksheets
),这是运行时行为:
的数据值必须是对支持实现定义的枚举的外部对象的对象引用 接口。 是让分配的或 设置为分配给<集合>中第一个元素 实施定义的方式。 - 设置
后,将执行 。如果存在 ,则将其执行。 - 一旦
和 完成(如果存在),就将 设置为分配给实现中 中的下一个元素-确定的方式。如果 中没有其他元素,则 的执行立即完成。否则,将再次执行 ,然后执行 (如果存在),然后重复此步骤。
在您的情况下,这基本上可以归结为:编译器会生成一个隐式调用,该调用在首次进入循环以及每次迭代时都会执行以下分配。编译后,将与此类似(实际上比检查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
请注意,这就是为什么在迭代集合时不要更改集合的内容的原因-通过调用[_NewEnum]
检索下一个元素。这意味着基础集合返回的项一次被“屈服”,因此在循环内更改集合的内容将影响返回的项,或者可能导致错误({{1}的行为}特定于实现)。
答案 1 :(得分:2)
这里, ws
参考用于一个一个地获取ActiveWorkbook工作表。
For Each ws In ActiveWorkbook.Worksheets
中对以上
ws
的引用
Dim ws As worksheet
不初始化对象,但声明将 ws
变量用作工作表。
答案 2 :(得分:1)
好的,多亏了蒂姆的回答,我才知道了:
我们可以使用Set ws = Worksheets(1)来初始化ws变量,但是对于For Each Next循环则没有必要,因为它会在每次迭代时都设置变量(并且总是基于项目的索引号。