在Rubberduck 2.0.11.2453中运行代码检查后,有4个Range引用被标记为:
会员'范围'隐式引用ActiveSheet
有问题的范围是指命名范围。是否有必要限定命名范围参考?
Private Sub RunORatio(ByVal TabNum As Integer)
Dim Start As Integer, Cat As Integer, iMth As Integer, CurrentRow As Integer, Report As Integer
Dim wsORatio As Worksheet, wsData As Worksheet, wsMacro As Worksheet
Dim sMap As String, Test As String
With ActiveWorkbook
Set wsMacro = .Worksheets("Macro")
Set wsORatio = .Worksheets("ORatio" & TabNum)
With wsORatio
sMap = "oratio" & TabNum & "map"
For CurrentRow = 1 To Range(sMap).Rows.Count '<---1 here
Test = Range(sMap).Cells(CurrentRow, 1) '<---1 Here
Set wsData = ActiveWorkbook.Worksheets(Test)
Start = Range(Range(sMap).Cells(CurrentRow, 2)).Row '<---2 Here
Report = wsMacro.Range(sMap).Cells(CurrentRow, 3)
For Cat = 0 To 12
For iMth = 1 To 12
wsORatio.Cells(Report + Cat, 7 + iMth) = wsData.Cells(Start + Cat, 37 + iMth)
Next iMth
Next Cat
Next CurrentRow
End With
End With
End Sub
答案 0 :(得分:10)
免责声明:我积极参与Rubberduck的开发。
考虑这个常见的错误:
lastRow = Worksheets("Sheet12").Cells(1, Rows.Count).End(xlUp).Row
Rows
不合格,因此隐式引用活动工作表,因此Rows.Count
不一定是“Sheet12”上的行数。代码可能有效,但它也可能导致一个微妙的错误,lastRow
因此而没有正确的值,具体取决于活动工作表的内容。
或者这个:
ActiveWorkbook.Worksheets("SummarySheet") _
.ListObjects("Table1").Sort.SortFields.Add _
Key:=Range("Table1[[#All],["Date]]"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortTextAsNumbers
看到了吗?由于Key
参数未限定,因此调用将在运行时失败,错误为1004 - “对象'_Global'的”方法'范围'失败“。那是169 Stack Overflow questions。 “错误1004”产生1465 Stack Overflow questions。
对活动工作表的隐式引用是导致错误的常见原因。
Rubberduck的VBA代码检查与ReSharper的C#静态代码分析一样,提示/建议。该工具告诉您,可能可能导致问题,或者使代码不那么明确。
当然不是 - Rubberduck只是让你知道在这些情况下,Range
被隐含地被引用,这就是它的全部内容。
你总是可以使用“忽略一次”快速修复来告诉Rubberduck“看,我知道我在做什么”:
“修复”插入特殊注释(内部,Rubberduck称之为“注释”),指示检查忽略特定结果,同时启用检查:
ActiveSheet
这些注释的优势在于告诉读者(未来你或者任何接受你代码的人),这里有一些东西。
未来版本最终将支持在模块级别指定 With ActiveWorkbook
Set wsMacro = .Worksheets("Macro")
Set wsORatio = .Worksheets("ORatio" & TabNum)
With wsORatio
sMap = "oratio" & TabNum & "map"
'@Ignore ImplicitActiveSheetReference
For CurrentRow = 1 To Range(sMap).Rows.Count
'@Ignore ImplicitActiveSheetReference
Test = Range(sMap).Cells(CurrentRow, 1)
Set wsData = ActiveWorkbook.Worksheets(Test)
'@Ignore ImplicitActiveSheetReference
Start = Range(Range(sMap).Cells(CurrentRow, 2)).Row
Report = wsMacro.Range(sMap).Cells(CurrentRow, 3)
For Cat = 0 To 12
For iMth = 1 To 12
wsORatio.Cells(Report + Cat, 7 + iMth) = wsData.Cells(Start + Cat, 37 + iMth)
Next iMth
Next Cat
Next CurrentRow
End With
End With
注释,以忽略整个模块中特定检查的所有结果。
请注意,检查属于可维护性和可读性问题类别。 @Ignore
不像以下那样明确且安全无虞:
Range("DefinedName")
它为您提供相同的范围,和读取它实际上是在工作簿级别提取范围的命名范围。
答案 1 :(得分:8)
免责声明:我也参与了Rubberduck的开发。
正如@ Mat'sMug已经指出的那样,检查会为您提供有关代码的信息。您使用该信息做的内容是偏好,编码信号等。
在这种情况下,检查并未告诉您代码中存在错误,它告诉您隐式对象引用可能会导致代码以意外方式运行。 此特定检查的结果是您让Excel决定如何解释您所指的对象。
来自Application.Range
的{{1}}:
在没有对象限定符的情况下使用时,此属性是一个快捷方式 ActiveSheet.Range(它返回活动工作表中的范围;如果是 活动工作表不是工作表,属性失败)。
最后一句话是你不应该忽视这一检查的第一个原因 - 如果你选择了Range
,那么没有限定符的Chart
将会抛出。如果您要分配给Workbook.Worksheets(foo)
(这实际上是the documentation),这就是为什么您应该使用Workbook.Sheets(foo)
代替Worksheet
的原因。
第二个原因与第一个原因有关。正如您在评论中正确指出的那样,“命名范围有一个工作表名称作为其引用的一部分”,或者改写为“命名范围可以假定为唯一”。但是如果您打开多个工作簿,那么不必须是唯一的。由于ActiveSheet
在调用时从ActiveWorkbook
返回 始终 ,如果代码所需的Workbook
未激活,此代码将抛出或返回不正确的Range
(如果其他Workbook
巧合地包含具有相同名称的Range
。
请注意,虽然With
块捕获了对ActiveWorkbook
的硬引用,但Application
没有 - 并且ActiveWorkbook
可能会在代码执行过程中发生变化。这一切都与您应该避免使用ActiveFoo
对象的原因有关 - 它们引入了错误将由“正确”代码引起的可能性。
解决方案很简单。只需在它们前面添加点。这样他们就会通过Workbook.Range
块而不是With
通过捕获的硬引用来调用Application.Range
。