使用 Resume Next 在循环中处理错误

Error handling in a loop using Resume Next

作为 VBA 的新手,我们将不胜感激。我的程序的基本要点是遍历电子表格的列并计算指定范围内每列中非空白单元格的数量。 这是我的电子表格的示例。

1 2 3
1 thing
2 thing
3 thing

当列中的所有单元格均为空白时,VBA 抛出 1004 错误,未找到单元格。我想做的是说,如果发生 1004 错误,将非空白单元格 (nonBlank = 0) 的计数设置为零,如果没有发生错误,则正常计数。在类似 Python 的情况下,我会使用 try/except。这是我的尝试。

For i = 1 To 3
    
    On Error Resume Next
    Set selec_cells = Sheet1.Range(Sheet1.Cells(FirstRow, i), Sheet1.Cells(LastRow, i)).SpecialCells(xlCellTypeVisible).Cells.SpecialCells(xlCellTypeConstants)
    
    If Err.Number <> 1004 Then
        nonBlank = 0
    Else
        nonBlank = selec_cells.Count
    End If
    On Error GoTo -1

 Next i

我的问题是,当我 运行 这段代码时,它每次都输出 0,即使第 2 列应该 return 3。谢谢!

编辑:selec_cells 是抛出错误的原因。

错误处理

  • VBA, it's a VB 中没有 On Error Goto -1(那些是指向不同页面的链接)。一个提示是,如果你 google VBA 东西,只需将 VBA 放在你要找的东西前面。

  • 当使用 On Error Resume Next(延迟错误捕获)时,您应该 'apply' 最多在一两行上使用 'close' 和 On Error Goto 0 (禁用错误捕获)或使用另一个错误处理程序。

  • 您对 On Error Resume Next 的使用是不可接受的,因为在这种特殊情况下我们可以测试范围:1. 延迟错误处理,2. 尝试设置范围,3. 禁用错误处理.如果出现错误,则不会设置范围,因此 If Not rg Is Nothing Then 可以转换为 'If rg Is Something Then'(双重否定)或 If已创建对范围的引用 Then.

  • 第二种解决方案说明了一种情况,其中主错误处理程序正在处理所有错误,但 SpecialCells 错误具有自己的错误处理程序。 Resume Next 表示继续发生错误的行之后的行。注意 Exit Sub 行并注意 Resume ProcExit 代码被重定向到标签的位置。

  • 以下说明了处理此问题的两种方法。在这个阶段,我建议您使用第一个,并记住在使用 On Error Resume Next(一两行)时使用 'closing' On Error Goto 0

代码

Option Explicit

Sub testOnErrorResumeNext()
    
    Const FirstRow As Long = 2
    Const LastRow As Long = 11
    
    Dim rg As Range ' ... additionally means 'Set rg = Nothing'.
    Dim nonBlank As Long ' ... additionally means 'nonBlank = 0'.
    Dim j As Long
    
    For j = 1 To 3 ' Since it's a column counter, 'j' or 'c' seems preferred.
        
        ' Since you're in a loop, you need the following line.
        Set rg = Nothing
        On Error Resume Next
        Set rg = Sheet1.Range(Sheet1.Cells(FirstRow, j), _
            Sheet1.Cells(LastRow, j)).SpecialCells(xlCellTypeVisible) _
            .Cells.SpecialCells(xlCellTypeConstants) 
        On Error GoTo 0
        
        If Not rg Is Nothing Then
            nonBlank = rg.Cells.Count
        Else
            ' Since you're in a loop, you need the following line.
            nonBlank = 0
        End If
    
        Debug.Print nonBlank

    Next j

End Sub

Sub testOnError()
    
    On Error GoTo clearError
    
    Const FirstRow As Long = 2
    Const LastRow As Long = 11
    
    Dim rg As Range ' ... additionally means 'Set rg = Nothing'.
    Dim nonBlank As Long ' ... additionally means 'nonBlank = 0'.
    Dim j As Long
    
    For j = 1 To 3 ' Since it's a column counter, 'j' or 'c' seems preferred.
        
        ' Since you're in a loop, you need the following line.
        Set rg = Nothing
        On Error GoTo SpecialCellsHandler
        Set rg = Sheet1.Range(Sheet1.Cells(FirstRow, j), _
            Sheet1.Cells(LastRow, j)).SpecialCells(xlCellTypeVisible) _
            .Cells.SpecialCells(xlCellTypeConstants) 
        On Error GoTo clearError
        
        If Not rg Is Nothing Then
            nonBlank = rg.Cells.Count
        End If
          
        Debug.Print nonBlank

    Next j
    
ProcExit:
    Exit Sub ' Note this.

SpecialCellsHandler:
    ' Since you're in a loop, you need the following line.
    nonBlank = 0
    Resume Next

clearError:
    MsgBox "Run-time error '" & Err.Number & "': " & Err.Description
    Resume ProcExit

End Sub

我的偏好是尽可能将可能导致错误的代码行封装在其自身的功能中。函数 returns true 或 false 指示是否有错误,输出参数用于 return 您想要的值。

这将错误测试限制在一个非常短且定义明确的函数中。


Sub ttest()

    Dim mySheet As Excel.Worksheet
    Set mySheet = ThisWorkbook.Sheet1
    
    Dim myIndex As Long
    Dim myNonBlank as long
    For myIndex = 1 To 3
    
        If AllCellsAreBlank(mySheet.Range(ThisWorkbook.Sheet1.Cells(myFirstRow, myIndex), mySheet.Cells(myLastRow, myIndex)), myIndex, mySelectCells) Then
        
            myNonBlank = 0
            
        Else
        
            myNonBlank = mySelectCells.Count
            
        End If
        
    Next
    
End Sub



Public Function AllCellsAreBlank(ByRef ipRange As Excel.Range, ByVal ipIndex As Long, ByRef opSelectCells As Range) As Boolean

    On Error Resume Next
    set opSelectCells = ipRange.SpecialCells(xlCellTypeVisible).Cells.SpecialCells(xlCellTypeConstants)
    AllCellsAreBlank = Err.Number <> 0
    On Error GoTo 0
    
End Function

作为参考,我使用的前缀是

  • ip: 仅用于输入参数
  • iop: 对于将被方法改变的输入参数
  • op: 对于一个参数只用于return一个值
  • my:方法中声明的任何变量。

我还建议您养成使用有意义的描述性名称的习惯,myRow、myCol 比 i、j 更有意义,并确保您使用完全限定的引用而不是隐式使用活动表。