Szukaj…


Unikanie warunków błędu

Gdy wystąpi błąd środowiska wykonawczego, dobry kod powinien go obsłużyć. Najlepszą strategią obsługi błędów jest pisanie kodu, który sprawdza warunki błędu i po prostu unika wykonywania kodu, który powoduje błąd w czasie wykonywania.

Jednym z kluczowych elementów w ograniczaniu błędów w czasie wykonywania jest pisanie małych procedur, które robią jedną rzecz . Im mniej powodów do niepowodzenia procedur, tym łatwiej debugować kod jako całość.


Unikanie błędu czasu wykonania 91 - Nie ustawiono obiektu lub zmiennej blokowej:

Ten błąd zostanie zgłoszony, gdy obiekt zostanie użyty przed przypisaniem jego odwołania. Można mieć procedurę, która odbiera parametr obiektu:

Private Sub DoSomething(ByVal target As Worksheet)
    Debug.Print target.Name
End Sub

Jeśli obiektowi target nie zostanie przypisane odwołanie, powyższy kod spowoduje błąd, którego można łatwo uniknąć, sprawdzając, czy obiekt zawiera rzeczywiste odwołanie do obiektu:

Private Sub DoSomething(ByVal target As Worksheet)
    If target Is Nothing Then Exit Sub
    Debug.Print target.Name
End Sub

Jeśli target nie przypisano odwołania, nieprzypisane odwołanie nigdy nie jest używane i nie występuje błąd.

Ten sposób wczesnego wyjścia z procedury, gdy jeden lub więcej parametrów jest nieprawidłowy, nazywa się klauzulą ochronną .


Unikanie błędu czasu wykonywania 9 - Indeks dolny poza zakresem:

Ten błąd jest zgłaszany, gdy dostęp do tablicy znajduje się poza jej granicami.

Private Sub DoSomething(ByVal index As Integer)
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

Biorąc pod uwagę indeks większy niż liczba arkuszy w ActiveWorkbook , powyższy kod spowoduje błąd w czasie wykonywania. Prosta klauzula ochronna może tego uniknąć:

Private Sub DoSomething(ByVal index As Integer)
    If index > ActiveWorkbook.Worksheets.Count Or index <= 0 Then Exit Sub
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

Większości błędów środowiska wykonawczego można uniknąć, dokładnie sprawdzając wartości, których używamy, zanim ich użyjemy, i odpowiednio rozgałęziając się na innej ścieżce wykonania, używając prostej instrukcji If - w klauzulach ochronnych, które nie przyjmują żadnych założeń i sprawdzają parametry procedury, a nawet w zbiór większych procedur.

Oświadczenie o błędzie

Nawet w przypadku klauzul ochronnych nie zawsze można realistycznie uwzględnić wszystkie możliwe błędy, które mogą wystąpić w toku procedury. Instrukcja On Error GoTo instruuje VBA, aby przejść do etykiety linii i przejść do „trybu obsługi błędów” za każdym razem, gdy wystąpi nieoczekiwany błąd w czasie wykonywania. Po usunięciu błędu kod może zostać wznowiony do „normalnego” wykonania przy użyciu słowa kluczowego Resume .

Etykiety linii oznaczają podprogramy : ponieważ podprogramy pochodzą ze starszego kodu BASIC i używają skoków GoTo i GoSub oraz instrukcji Return celu powrotu do rutyny „głównej”, dość łatwo jest napisać trudny do przestrzegania kod spaghetti, jeśli nie ma ściśle określonej struktury . Z tego powodu najlepiej:

  • procedura ma jeden i tylko jeden podprogram obsługi błędów
  • podprogram obsługi błędów działa zawsze tylko w stanie błędu

Oznacza to, że procedura, która obsługuje swoje błędy, powinna mieć następującą strukturę:

Private Sub DoSomething()
    On Error GoTo CleanFail

    'procedure code here

CleanExit:
    'cleanup code here
    Exit Sub

CleanFail:
    'error-handling code here
    Resume CleanExit
End Sub

Strategie obsługi błędów

Czasami chcesz obsługiwać różne błędy za pomocą różnych działań. W takim przypadku sprawdzisz globalny obiekt Err , który będzie zawierał informacje o zgłoszonym błędzie - i podejmie odpowiednie działania:

CleanExit:
    Exit Sub

CleanFail:
    Select Case Err.Number
        Case 9
            MsgBox "Specified number doesn't exist. Please try again.", vbExclamation
            Resume
        Case 91
            'woah there, this shouldn't be happening.
            Stop 'execution will break here
            Resume 'hit F8 to jump to the line that raised the error
        Case Else
            MsgBox "An unexpected error has occurred:" & vbNewLine & Err.Description, vbCritical
            Resume CleanExit
    End Select
End Sub

Jako ogólną wskazówkę rozważ włączenie obsługi błędów dla całego podprogramu lub funkcji i obsłuż wszystkie błędy, które mogą wystąpić w jego zakresie. Jeśli potrzebujesz obsługiwać błędy tylko w małej sekcji kodu - włącz i wyłącz obsługę błędów na tym samym poziomie:

Private Sub DoSomething(CheckValue as Long)

    If CheckValue = 0 Then
        On Error GoTo ErrorHandler   ' turn error handling on
        ' code that may result in error
        On Error GoTo 0              ' turn error handling off - same level
    End If

CleanExit:
    Exit Sub

ErrorHandler:
    ' error handling code here
    ' do not turn off error handling here
    Resume

End Sub

Numery linii

VBA obsługuje starsze numery (np. QBASIC). Właściwość ukryta Erl może być wykorzystana do identyfikacji numeru linii, który wywołał ostatni błąd. Jeśli nie używasz numerów linii, Erl zwróci tylko 0.

Sub DoSomething()
10 On Error GoTo 50
20 Debug.Print 42 / 0
30 Exit Sub
40
50 Debug.Print "Error raised on line " & Erl ' returns 20
End Sub

Jeśli używasz numery linii, ale nie konsekwentnie, a następnie Erl powróci ostatni numer wiersza przed instrukcją, że podniesiony błąd.

Sub DoSomething()
10 On Error GoTo 50
   Debug.Print 42 / 0
30 Exit Sub

50 Debug.Print "Error raised on line " & Erl 'returns 10
End Sub

Pamiętaj, że Erl także tylko precyzję liczb Integer i po cichu się przepełni. Oznacza to, że numery linii poza zakresem liczb całkowitych dadzą nieprawidłowe wyniki:

Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
      Debug.Print Erl   'Prints 34462
End Sub

Numer linii nie jest tak istotny jak stwierdzenie, które spowodowało błąd, a linie numeracyjne szybko stają się żmudne i niełatwe w utrzymaniu.

Wznów słowo kluczowe

Podprogram obsługi błędów:

  • uruchom do końca procedury, w którym to przypadku wykonanie zostanie wznowione w procedurze wywołującej.
  • lub użyj słowa kluczowego Resume aby wznowić wykonywanie w ramach tej samej procedury.

Resume kluczowe Resume powinno być zawsze używane tylko w podprogramie obsługi błędów, ponieważ jeśli VBA napotka Resume bez bycia w stanie błędu, pojawi się błąd czasu wykonywania 20 „Resume without error”.

Istnieje kilka sposobów, w jakie podprogram obsługujący błędy może użyć słowa kluczowego Resume :

  • Resume używane samodzielnie, wykonywanie jest kontynuowane na instrukcji, która spowodowała błąd . Jeśli błąd nie zostanie faktycznie naprawiony przed zrobieniem tego, wtedy ten sam błąd zostanie ponownie zgłoszony, a wykonanie może wejść w nieskończoną pętlę.
  • Resume Next kontynuuje wykonywanie instrukcji, która następuje bezpośrednio po instrukcji, która spowodowała błąd. Jeśli błąd nie zostanie właściwie obsługiwane zanim to zrobi, wtedy wykonanie jest dopuszczalne, aby kontynuować potencjalnie nieprawidłowych danych, które mogą wynikać z błędów logicznych i nieoczekiwanego zachowania.
  • Resume [line label] kontynuuje wykonywanie przy określonej etykiecie linii (lub numerze linii, jeśli używasz starszych linii). Zwykle pozwala to na wykonanie kodu czyszczącego przed czystym wyjściem z procedury, na przykład upewnienie się, że połączenie z bazą danych jest zamknięte przed powrotem do programu wywołującego.

Po błędzie Wznów dalej

Sama instrukcja On Error może użyć słowa kluczowego Resume aby poinstruować środowisko wykonawcze VBA, aby skutecznie ignorowało wszystkie błędy .

Jeśli błąd nie zostanie naprawiony przed wykonaniem tego, wówczas wykonywanie może być kontynuowane z potencjalnie nieprawidłowymi danymi, co może powodować błędy logiczne i nieoczekiwane zachowanie .

Podkreślenia powyżej nie można wystarczająco podkreślić. Po błędzie Wznów dalej skutecznie ignoruje wszystkie błędy i wsuwa je pod dywan . Program, który wysadza się z błędem środowiska wykonawczego z nieprawidłowymi danymi wejściowymi, jest lepszym programem niż ten, który działa z nieznanymi / niezamierzonymi danymi - czy to tylko dlatego, że błąd jest znacznie łatwiejszy do zidentyfikowania. On Error Resume Next można łatwo ukryć błędy .

Instrukcja On Error ma zasięg proceduralny - dlatego w danej procedurze zwykle powinna znajdować się tylko jedna , pojedyncza taka instrukcja On Error .

Czasami jednak nie można całkowicie uniknąć błędu, a przeskakiwanie do podprogramu obsługującego błędy tylko do opcji Resume Next po prostu nie wydaje się właściwe. W tym konkretnym przypadku instrukcję, o której wiadomo, że może się nie powieść, można zawinąć między dwie instrukcje On Error :

On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0

Instrukcja On Error GoTo 0 resetuje obsługę błędów w bieżącej procedurze, tak że wszelkie dalsze instrukcje powodujące błąd środowiska wykonawczego byłyby nieobsługiwane w ramach tej procedury i zamiast tego przekazywały stos wywołań, dopóki nie zostaną złapane przez aktywną procedurę obsługi błędów. Jeśli w stosie wywołań nie ma aktywnej procedury obsługi błędów, będzie traktowany jako nieobsługiwany wyjątek.

Public Sub Caller()
    On Error GoTo Handler
    
    Callee
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Caller."
End Sub

Public Sub Callee()
    On Error GoTo Handler
    
    Err.Raise 1     'This will be handled by the Callee handler.
    On Error GoTo 0 'After this statement, errors are passed up the stack.
    Err.Raise 2     'This will be handled by the Caller handler.    
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Callee."
    Resume Next
End Sub

Błędy niestandardowe

Często, pisząc wyspecjalizowaną klasę, będziesz chciał, aby zgłaszała ona swoje własne specyficzne błędy i będziesz potrzebować czystego sposobu, aby użytkownik / kod wywołujący obsługiwał te niestandardowe błędy. Świetnym sposobem na osiągnięcie tego jest zdefiniowanie dedykowanego typu Enum :

Option Explicit
Public Enum FoobarError
    Err_FooWasNotBarred = vbObjectError + 1024
    Err_BarNotInitialized
    Err_SomethingElseHappened
End Enum

Korzystanie z wbudowanej stałej vbObjectError zapewnia, że niestandardowe kody błędów nie pokrywają się z zarezerwowanymi / istniejącymi kodami błędów. Tylko pierwsza wartość wyliczenia musi być wyraźnie określona, ponieważ podstawowa wartość każdego elementu Enum jest o 1 większa niż poprzedniego elementu, więc podstawowa wartość Err_BarNotInitialized jest domyślnie vbObjectError + 1025 .

Podnoszenie własnych błędów w czasie wykonywania

Błąd środowiska wykonawczego można wywołać za pomocą instrukcji Err.Raise , więc niestandardowy błąd Err_FooWasNotBarred można wywołać w następujący sposób:

Err.Raise Err_FooWasNotBarred

Metoda Err.Raise może również przyjmować niestandardowe parametry Description i Source - z tego powodu dobrym pomysłem jest także zdefiniowanie stałych, które będą przechowywać opis każdego błędu niestandardowego:

Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."

A następnie utwórz dedykowaną prywatną metodę zgłaszania każdego błędu:

Private Sub OnFooWasNotBarredError(ByVal source As String)
    Err.Raise Err_FooWasNotBarred, source, Msg_FooWasNotBarred
End Sub

Private Sub OnBarNotInitializedError(ByVal source As String)
    Err.Raise Err_BarNotInitialized, source, Msg_BarNotInitialized
End Sub

Implementacja klasy może następnie wywołać te wyspecjalizowane procedury w celu zgłoszenia błędu:

Public Sub DoSomething()
    'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
    If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
    '...
End Sub

Kod klienta może następnie obsługiwać Err_BarNotInitialized tak jak każdy inny błąd, w ramach własnego podprogramu obsługi błędów.


Uwaga: starsze słowo kluczowe Error może być również użyte zamiast Err.Raise , ale jest przestarzałe / przestarzałe.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow