Есть ли способ программно определить, была ли включена обработка ошибок в VBA? - PullRequest
1 голос
/ 27 марта 2020

Фон

Привет! Я застрял, пытаясь сделать выбор между эффективностью и надежностью. Это вопрос VBA в Excel.

У меня есть функция GetTable (ниже), которая берет имя таблицы и возвращает соответствующий объект ListObject в моей книге. Я предпочитаю, чтобы этот метод явно вызывал ThisWorkbook.Worksheet.ListObjects("strTableName"), потому что он обеспечивает гибкость кодирования, если таблицы перемещаются на разные листы.

Первоначально это было сделано путем циклического просмотра каждого рабочего листа и проверки того, имеет ли каждый объект ListObject совпадающее имя с предоставленный вход. Это Option 1 кода ниже. Этот метод работает достаточно хорошо, хотя он не особенно эффективен и может замедлить работу, если у вас большая рабочая книга со множеством листов и таблиц, и вы захватываете несколько таблиц в своем макросе.

Чтобы повысить эффективность, я изменил на Option 2, которая явно вызывает таблицу на каждом листе. Если таблица не существует, она выдает ошибку, и обработка ошибок позволяет ей перейти непосредственно к следующему рабочему листу. Это прекрасно работает для обычного использования книги, когда включена обработка ошибок. Однако, когда обработка ошибок отключена во время отладки, это становится проблемой, потому что код всегда будет зависать здесь. включен или выключен, чтобы функция могла переключаться между этими двумя методами в зависимости от случая? Я понимаю, что это может быть рискованно, потому что во время отладки он будет проходить через другой код, но я все же хотел бы знать, возможно ли это.

Если нет, есть ли другой способ, которым я могу выполнить sh a метод, аналогичный варианту № 2 (или более эффективный, если у вас есть идея!) без выдачи ошибки?

Я знаю, что для этой функции повышение эффективности часто не будет иметь значения, если вы не смешны количество таблиц, но я делюсь этим как часть большей библиотеки функций для менее опытных коллег, поэтому они могут не использовать это эффективно и включать в большие циклы. Также поиск правильного подхода может пригодиться для применения к другим функциям.

Заранее спасибо и крепкого здоровья!

Код

Function GetTable(strTableName As String) As ListObject
'This function searches the workbook for a table with the exact name strTableName
'Returns the table object
'If nothing found then display message
On Error Resume Next

Dim sht As Worksheet
Dim tbl As ListObject

'#Option 1: Slower but doesn't throw errors
'For Each sht In ThisWorkbook.Worksheets
'    For Each tbl In sht.ListObjects
'        'Debug.Print sht.Name & " " & tbl.Name      'uncomment to print all table names
'
'        If LCase(tbl.Name) = LCase(strTableName) Then
'            Set GetTable = tbl
'            Exit Function
'        End If
'    Next tbl
'Next sht

'#Option 2: More efficient but causes problems when debugging
For Each sht In ThisWorkbook.Worksheets

    Set GetTable = sht.ListObjects(strTableName) 'Generates runtime error 9 if table doesn't exist on sheet

    If Err.Number = 0 Then Exit Function  'No error means we've found the answer

    Err.Clear

Next sht

'If the code reaches this point it means the table wasn't found.
'This may have negative implications depending on where this function is called.
'This message gives the user an out
Dim ans As Byte
ans = MsgBox("Could not find table with name '" & strTableName & "'." & vbNewLine & vbNewLine & _
      "Would you like to abort code?", vbCritical + vbYesNo, "Table not found")

If ans = vbYes Then End

'Set GetTable = Nothing   '#This is redundant

End Function

Ответы [ 3 ]

2 голосов
/ 28 марта 2020

Это то, что я имею в виду под кешем:

Function GetTable(ByVal strTableName As String, _
                         Optional reset As Boolean = False) As ListObject
    Static dict As Object 'Static, so persists between calls
    Dim sht As Worksheet
    Dim tbl As ListObject, nm
    If reset Then Set dict = Nothing  '<< clear the cache
    If dict Is Nothing Then
        Set dict = CreateObject("scripting.dictionary")
        For Each sht In ThisWorkbook.Worksheets
            For Each tbl In sht.ListObjects
                nm = LCase(tbl.Name)
                If Not dict.exists(nm) Then dict.Add nm, tbl
            Next tbl
        Next sht
    End If
    strTableName = LCase(strTableName)
    If dict.exists(strTableName) Then Set GetTable = dict(strTableName)
End Function

При первом вызове он сканирует все списки объектов, но после этого он будет использовать словарь для поиска.

Вам необходимо знать, когда вам может понадобиться очистить кэш, чтобы учесть добавленные или удаленные списочные объекты.

1 голос
/ 27 марта 2020

Еще как комментарий к вашему вопросу о более эффективном подходе ....

Мне лично не нравится этот подход из-за безоговорочного вызова Range и Activate, но вы, возможно, сможете использовать функцию, подобную следующей, вместо циклического перебора всех таблиц и таблиц:

Private Function GetTable(ByVal TableName As String, Optional ByVal wb as Workbook) As ListObject
    If wb Is Nothing Then Set wb = ThisWorkbook
    wb.Activate
    Set GetTable = Range(TableName).ListObject
End Function

Конечно, сделать это более надежным, если в нем нет таблицы с таким именем. рабочая тетрадь ... (что в основном означает, что я не ответил на суть вашего вопроса).

1 голос
/ 27 марта 2020

Заменить If ans = vbYes Then End на Заменить If ans = vbYes Then Exit Function, потому что End является кнопкой «самоуничтожения» для кода. Обратитесь к документации MS для дальнейшего чтения.

Оператор End резко останавливает выполнение кода, не вызывая событие Unload, QueryUnload или Terminate, или любое другое Visual Basi * 1017. * код Код, который вы поместили в события Unload, QueryUnload и Terminate форм и модулей классов, не выполняется. Объекты, созданные из модулей классов, уничтожаются, файлы, открываемые с помощью оператора Open, закрываются, а память, используемая вашей программой, освобождается. Ссылки на объекты, хранящиеся в других программах, становятся недействительными.

PS Если вы говорите об опции ошибки, которую можно установить через VBE, вам может не повезти, см. Установите соответствующий уровень обработки ошибок

...