代码检查 - 命名范围参考

时间:2017-02-01 13:32:24

标签: vba rubberduck

在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

2 个答案:

答案 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“看,我知道我在做什么”:

ignore once

“修复”插入特殊注释(内部,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