7

在 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
4

2 回答 2

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' 的方法 'Range' 失败”。那是169 个 Stack Overflow 问题。“错误 1004”产生1465 堆栈溢出问题

对活动工作表的隐式引用是错误的常见原因。


Rubberduck 的 VBA 代码检查类似于 ReSharper 的 C# 静态代码分析提示/建议。该工具告诉您,这里可能存在一些可能导致问题的东西,或者使代码变得不那么明确。

您是否需要完全符合每个Range电话的要求?当然不是——Rubberduck 只是让你知道,在这些情况下,ActiveSheet它被隐式引用,仅此而已。

你总是可以告诉 Rubberduck “看,我知道我在做什么”,使用“忽略一次”快速修复:

忽略一次

该“修复”插入了一个特殊注释(在内部,Rubberduck 称它们为“注释”),指示检查忽略特定结果,同时启用检查:

  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")不像以下那样明确和故障安全:

ActiveWorkbook.Names("DefinedName").RefersToRange

这为您提供了相同的范围,并且读起来就像它实际上是在拉取工作簿级别的命名范围。

于 2017-02-01T15:17:38.357 回答
8

免责声明:我还参与了 Rubberduck 的开发。

正如@Mat'sMug 已经指出的那样,检查正在为您提供有关代码的信息。您这些信息的处理取决于偏好、编码风格等。

在这种情况下,检查并没有告诉您代码中存在错误,而是告诉您隐式对象引用可能会导致您的代码以意想不到的方式运行。 此特定检查的要点是您让 Excel 决定如何解释您所指的对象。

文档Application.Range

在没有对象限定符的情况下使用时,此属性是 ActiveSheet.Range 的快捷方式(它从活动工作表返回一个范围;如果活动工作表不是工作表,则该属性失败)。

最后一句话是您不应该忽略此检查的第一个原因 -Range如果您有Chart选择,则没有限定符将抛出。这与您应该使用Workbook.Worksheets(foo)而不是Workbook.Sheets(foo)分配给 a的原因相同Worksheet(这实际上在 Rubberduck 检查愿望清单上)。

第二个原因与第一个有关。正如您在评论中正确指出的那样,“命名范围有一个工作表名称作为其引用的一部分”,或者改写为“可以假定命名范围是唯一的”。但是,如果您打开了多个工作簿,则它不必是唯一的。由于ActiveSheet总是从调用时返回,如果期望的代码不是活动的,则此代码将抛出或返回不正确的如果另一个巧合地包含具有相同名称的 a)。ActiveWorkbookWorkbookRangeWorkbookRange

请记住,尽管该With块捕获对 的硬引用ActiveWorkbookApplication但不会 - 并且ActiveWorkbook可以在代码执行过程中更改。这一切都与您应该避免使用ActiveFoo对象的原因有关 -它们引入了错误将由其他“正确”代码导致的可能性

解决方案很简单。只需在它们前面添加点即可。这样他们将Workbook.Range通过从With块中捕获的硬引用而不是Application.Range.

于 2017-02-01T16:07:14.507 回答