删除含#REF行的VBA代码循环内触发Run Time Error 91,移出正常求因
Run-time Error 91 原因分析与修复方案
我来帮你拆解一下问题所在——这个错误和嵌套循环有间接关系,但核心原因是你的代码逻辑存在几个关键漏洞,在遍历多工作表时被暴露了出来。
问题重现
你尝试删除所有包含#REF!的行,遍历所有工作表时触发Run-time Error 91(对象变量或With块变量未设置),但仅处理单个活动工作表时能正常运行。
无法正常运行的代码
Sub FindDeleteLoop() Dim wb1 As Workbook Dim sh1 As Worksheet Dim objcell As Range Dim delrow As Long Dim i As Long Set wb1 = ActiveWorkbook For Each sh1 In wb1.Worksheets With sh1 RowCount = sh1.Cells.Find(What:="*", After:=sh1.Cells(1, 1), LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Row For i = 1 To RowCount On Error GoTo nextcom Set objcell = sh1.Cells.Find(What:="*#REF*", After:=sh1.Cells(1, 1), LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False) objcell.EntireRow.Delete Next i nextcom: End With Next End Sub
可正常运行的代码
将遍历多工作表的循环替换为单工作表赋值:
Set sh1 = wb1.ActiveSheet ' 替换原For Each循环
错误原因解析
Find方法返回Nothing时未做判断
当某个工作表中已经没有#REF!内容时,sh1.Cells.Find(...)会返回Nothing,此时你直接执行objcell.EntireRow.Delete就会触发Error 91——因为你试图访问一个未赋值的对象变量。
单工作表运行时可能刚好存在#REF!内容,所以objcell能被正常赋值,错误没暴露;但遍历多工作表时,总会遇到没有#REF!的工作表,错误就触发了。内层
For i = 1 To RowCount循环完全多余且逻辑错误
你用Find方法本身就可以定位匹配项,这个固定次数的循环完全没必要:- 每次删除行后,工作表的实际行数会减少,但
RowCount是初始值,循环次数不会变化,会做很多无效遍历。 - 每次循环都用
SearchDirection:=xlPrevious找最后一个匹配项,删除后下一次循环还是找最后一个,直到删完,之后就会返回Nothing触发错误。
- 每次删除行后,工作表的实际行数会减少,但
错误处理的位置不合理
你的错误处理标签nextcom在Next i之后,当错误触发时,会直接跳出内层循环,进入下一个工作表的处理——但这只是掩盖了问题,没有从根本上解决objcell为Nothing的情况。
修复后的代码
正确的做法是用Find+FindNext遍历所有匹配项,或者收集所有需要删除的行再一次性删除(避免行号变化的问题)。这里提供两种方案:
方案1:用FindNext遍历并删除(从下往上删)
Sub DeleteRefRows() Dim wb1 As Workbook Dim sh1 As Worksheet Dim objcell As Range Dim firstFound As String Set wb1 = ActiveWorkbook For Each sh1 In wb1.Worksheets With sh1 ' 查找第一个包含#REF!的单元格 Set objcell = .Cells.Find(What:="*#REF*", LookIn:=xlFormulas, LookAt:=xlPart, _ SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False) If Not objcell Is Nothing Then ' 确保找到匹配项 firstFound = objcell.Address ' 记录第一个匹配项的地址,避免无限循环 Do ' 从下往上删,避免行号错乱 .Rows(objcell.Row).Delete ' 查找下一个匹配项 Set objcell = .Cells.FindNext(After:=objcell) ' 防止循环回到第一个匹配项 If objcell Is Nothing Then Exit Do Loop Until objcell.Address = firstFound End If End With Next sh1 End Sub
方案2:用Union收集所有行后一次性删除(更高效)
Sub DeleteRefRowsUnion() Dim wb1 As Workbook Dim sh1 As Worksheet Dim objcell As Range Dim deleteRange As Range Dim firstFound As String Set wb1 = ActiveWorkbook For Each sh1 In wb1.Worksheets Set deleteRange = Nothing ' 重置范围 With sh1 Set objcell = .Cells.Find(What:="*#REF*", LookIn:=xlFormulas, LookAt:=xlPart, _ SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False) If Not objcell Is Nothing Then firstFound = objcell.Address Do ' 将匹配行加入deleteRange If deleteRange Is Nothing Then Set deleteRange = .Rows(objcell.Row) Else Set deleteRange = Union(deleteRange, .Rows(objcell.Row)) End If Set objcell = .Cells.FindNext(After:=objcell) If objcell Is Nothing Then Exit Do Loop Until objcell.Address = firstFound ' 一次性删除所有收集的行 If Not deleteRange Is Nothing Then deleteRange.Delete End If End With Next sh1 End Sub
内容的提问来源于stack exchange,提问作者Eliseo Di Folco
相关产品推荐
相关产品推荐

