您在问题中显示的代码失败,原因是:
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
Assets 是一个工作表,它是一种对象,在为对象赋值时必须使用Set:
Set Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
这会失败,因为Array("…") 不是工作表。
您暗示您的代码的早期版本会运行,但不会在工作表中循环。原因是:
MsgBox ActiveSheet.Name
这会显示活动工作表的名称,但此循环中的任何内容都不会更改活动工作表。
我对您的解决方案不满意,尽管它没有明显的错误。我见过太多的程序失败,因为程序员在一个语句中做了太多。首先,越复杂的语句,一开始就做对的时间就越长,在后续的维护过程中也需要越长的时间来理解。有时,最初的程序员会稍微错误地表述;有时维护程序员在尝试更新它时会出错。在任何情况下,程序员花费的额外时间都不能证明运行时的任何节省是合理的。
Alex K 已修复您的代码,方法是根据 VBA 的要求将 Assets 和 Asset 重新定义为 Variants,并添加 Sheets(Asset).Select 以更改哪个工作表处于活动状态。我不能同意这一点,因为Select 是一个缓慢的声明。特别是,如果您不包含 Application.ScreenUpdating = False,则您的例程持续时间可能会随着从每个 Select 重新绘制屏幕而达到顶峰。
在解释我的解决方案之前,先了解一下变体的背景。
如果我写:
Dim I as Long
I 将始终是一个长整数。
在运行时,编译器/解释器在遇到I时不必考虑是什么:
I = I + 5
但假设我写:
Dim V as Variant
V = 5
V = V + 5
V = "Test"
V = V & " 1"
这是完全有效(有效但不合理)的代码,因为 Variant 可以包含数字、字符串或工作表。但是每次我的代码访问 V 时,解释器都必须检查 V 的当前内容的类型,并决定它是否适合当前情况。这很耗时。
我不想阻止您在适当的时候使用变体,因为它们可能非常有用,但您需要了解它们的开销。
接下来我希望提倡使用有意义和系统的名称。我根据我使用多年的系统命名我的变量。我可以查看我的任何程序/宏并知道变量是什么。当我需要更新我在 12 或 15 个月前编写的程序/宏时,这是一个实时节省程序。
我不喜欢:
Dim Assets As Variant
Assets = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
因为“pipe_mat_tables”等不是资产;它们是工作表的名称。我会写:
Dim WshtNames As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
我的第一个提议是:
Option Explicit
Sub Test1()
Dim WshtNames As Variant
Dim WshtNameCrnt As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For Each WshtNameCrnt In WshtNames
With Worksheets(WshtNameCrnt)
Debug.Print "Cell B1 of worksheet " & .Name & " contains " & .Range("B1").Value
End With
Next WshtNameCrnt
End Sub
我本可以将 WshtNameCrnt 命名为 WshtName,但我被告知名称应至少相差三个字符,以避免使用错误的字符而不会引起注意。
Array 函数返回一个包含数组的变量。 For Each 语句的控制变量必须是对象或变量。这就是我将WshtNames 和WshtNameCrnt 定义为变体的原因。请注意,您的解决方案有效,因为工作表是一个对象。
我使用了With Worksheets(WshtNameCrnt),这意味着匹配End With 之前的任何代码都可以通过在开头添加句点来访问此工作表的组件。所以.Name 和.Range("B1").Value 引用Worksheets(WshtNameCrnt) 而不选择工作表。这比任何替代方法都更快、更清晰。
我使用了Debug.Print 而不是MsgBox,因为它不那么麻烦。我的代码无需为每个工作表按 Return 即可运行,并且我在即时窗口中有一个整洁的列表,我可以在闲暇时检查它。在开发过程中,我的代码中经常有许多 Debug.Print 语句,这就是为什么我输出一个句子而不仅仅是工作表名称或单元格值的原因。
我的第二个奉献是:
Sub Test2()
Dim InxW As Long
Dim WshtNames As Variant
WshtNames = Array("pipe_mat_tables", "pipe_diam_tables", "pipe_length_tables")
For InxW = LBound(WshtNames) To UBound(WshtNames)
With Worksheets(WshtNames(InxW))
Debug.Print "Cell B1 of worksheet " & .Name & " contains " & .Range("B1").Value
End With
Next InxW
End Sub
此宏与第一个宏具有相同的效果。我有时会发现For 比For Each 更方便,尽管在这种情况下我看不出任何一种优势。请注意,我已经写了LBound(WshtNames),即使 WshtNames 的下限总是为零。这只是我(过度?过度?)精确。
希望这会有所帮助。