Suche…
Fehlerzustände vermeiden
Wenn ein Laufzeitfehler auftritt, sollte guter Code damit umgehen. Die beste Strategie zur Fehlerbehandlung besteht darin, Code zu schreiben, der auf Fehlerbedingungen prüft und einfach die Ausführung von Code vermeidet, der zu einem Laufzeitfehler führt.
Ein Schlüsselelement bei der Reduzierung von Laufzeitfehlern ist das Schreiben kleiner Prozeduren, die eine Sache tun . Je weniger Gründe für das Scheitern von Prozeduren erforderlich sind, desto einfacher ist es, den gesamten Code zu debuggen.
Laufzeitfehler vermeiden 91 - Objekt oder Mit Blockvariable nicht gesetzt:
Dieser Fehler wird ausgelöst, wenn ein Objekt verwendet wird, bevor seine Referenz zugewiesen wird. Möglicherweise verfügt eine Prozedur über einen Objektparameter:
Private Sub DoSomething(ByVal target As Worksheet)
Debug.Print target.Name
End Sub
Wenn dem target
keine Referenz zugewiesen wurde, löst der obige Code einen Fehler aus, der leicht vermieden werden kann, indem geprüft wird, ob das Objekt eine tatsächliche Objektreferenz enthält:
Private Sub DoSomething(ByVal target As Worksheet)
If target Is Nothing Then Exit Sub
Debug.Print target.Name
End Sub
Wenn dem target
keine Referenz zugewiesen ist, wird die nicht zugewiesene Referenz niemals verwendet und es tritt kein Fehler auf.
Diese Methode, eine Prozedur vorzeitig zu beenden, wenn ein oder mehrere Parameter nicht gültig sind, wird als Guard-Klausel bezeichnet .
Laufzeitfehler 9 vermeiden - Index außerhalb des gültigen Bereichs:
Dieser Fehler wird ausgelöst, wenn auf ein Array außerhalb seiner Grenzen zugegriffen wird.
Private Sub DoSomething(ByVal index As Integer)
Debug.Print ActiveWorkbook.Worksheets(index)
End Sub
Bei einem Index, der größer als die Anzahl der Arbeitsblätter im ActiveWorkbook
, führt der obige Code zu einem Laufzeitfehler. Eine einfache Schutzklausel kann das vermeiden:
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
Die meisten Laufzeitfehler können vermieden werden, indem die Werte, die wir verwenden, sorgfältig überprüft werden, bevor wir sie verwenden, und entsprechend in einen anderen Ausführungspfad verzweigt werden. Verwenden Sie dazu eine einfache If
Anweisung, die keine Annahmen macht und die Parameter einer Prozedur oder sogar die der Prozeduren validiert Körper der größeren Verfahren.
On Error-Anweisung
Selbst mit Wachklauseln kann man nicht realistisch immer alle möglichen Fehlerbedingungen berücksichtigen, die im Körper einer Prozedur auftreten können. Die On Error GoTo
Anweisung weist VBA an, zu einer Zeilenbezeichnung zu springen und den "Fehlerbehandlungsmodus" aufzurufen, wenn zur Laufzeit ein unerwarteter Fehler auftritt. Einen Fehler nach dem Umgang mit Code kann mit dem in „normale“ Ausführung wieder zurück Resume
Schlüsselwort.
Linienbezeichnungen Subroutinen bezeichnen: da Subroutinen von Legacy - BASIC - Code stammt und verwendet GoTo
und GoSub
Sprünge und Return
Aussagen zurück auf die „main“ Routine zu springen, es ist ziemlich einfach schwer zu folgen Spaghetti - Code zu schreiben , wenn die Dinge nicht streng strukturiert . Aus diesem Grund ist es am besten:
- Eine Prozedur hat nur ein Unterprogramm zur Fehlerbehandlung
- Das Unterprogramm zur Fehlerbehandlung läuft immer nur in einem Fehlerzustand
Dies bedeutet, dass eine Prozedur, die ihre Fehler behandelt, folgendermaßen strukturiert ist:
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
Fehlerbehandlungsstrategien
Manchmal möchten Sie verschiedene Fehler mit unterschiedlichen Aktionen behandeln. In diesem Fall werden Sie das globale Err
Objekt untersuchen, das Informationen zu dem aufgetretenen Fehler enthält - und entsprechend handeln:
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
Als allgemeine Richtlinie sollten Sie die Fehlerbehandlung für die gesamte Subroutine oder Funktion aktivieren und alle Fehler behandeln, die in ihrem Gültigkeitsbereich auftreten können. Wenn Sie nur Fehler im kleinen Codeabschnitt behandeln müssen, aktivieren Sie die Fehlerbehandlung auf derselben Ebene:
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
Linien Nummern
VBA unterstützt Zeilennummern (z. B. QBASIC) im Legacy-Stil. Die ausgeblendete Erl
Eigenschaft kann verwendet werden, um die Zeilennummer zu identifizieren, die den letzten Fehler ausgelöst hat. Wenn Sie keine Zeilennummern verwenden, gibt Erl
nur 0 zurück.
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
Wenn Sie Zeilennummern verwenden, aber nicht konsequent, dann Erl
wird wieder die letzte Zeilennummer vor der Anweisung, die den Fehler ausgelöst.
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
Denken Sie daran, dass Erl
auch nur eine Integer
Genauigkeit hat und lautlos überläuft. Dies bedeutet, dass Zeilennummern außerhalb des Ganzzahlbereichs falsche Ergebnisse liefern:
Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
Debug.Print Erl 'Prints 34462
End Sub
Die Zeilennummer ist nicht ganz so relevant wie die Aussage, die den Fehler verursacht hat, und Nummerierungszeilen werden schnell langweilig und nicht sehr wartungsfreundlich.
Schlüsselwort fortsetzen
Eine Fehlerbehandlungs-Subroutine wird entweder:
- bis zum Ende der Prozedur ausführen. In diesem Fall wird die Ausführung in der aufrufenden Prozedur fortgesetzt.
- oder verwenden Sie das Schlüsselwort
Resume
, um die Ausführung innerhalb derselben Prozedur fortzusetzen.
Das Resume
Schlüsselwort sollte nur innerhalb einer Fehlerbehandlungs-Subroutine verwendet werden, da der VBA-Fehler 20 "Resume ohne Fehler" auslöst, wenn VBA auf Resume
stößt, ohne sich in einem Fehlerzustand zu befinden.
Es gibt mehrere Möglichkeiten, wie ein Unterprogramm zur Fehlerbehandlung das Schlüsselwort Resume
:
-
Resume
für denResume
verwendet, wird die Ausführung mit der Anweisung fortgesetzt , die den Fehler verursacht hat . Wenn der Fehler tatsächlich , bevor Sie das gehandhabt wird , nicht, dann wird der gleiche Fehler wieder angehoben werden, und die Ausführung könnte eine Endlosschleife eingeben. -
Resume Next
setzt die Ausführung der Anweisung unmittelbar nach der Anweisung, die den Fehler verursacht hat, fort. Wenn der Fehler zuvor nicht wirklich behandelt wird, kann die Ausführung mit möglicherweise ungültigen Daten fortgesetzt werden, was zu logischen Fehlern und unerwartetem Verhalten führen kann. -
Resume [line label]
setzt die Ausführung an der angegebenen Zeilenbeschriftung (oder Zeilennummer, wenn Sie Zeilennummern im Legacy-Stil verwenden) fort. Dies würde normalerweise die Ausführung eines Bereinigungscodes ermöglichen, bevor die Prozedur sauber beendet wird, z. B. das Sicherstellen, dass eine Datenbankverbindung geschlossen wird, bevor zum Aufrufer zurückgekehrt wird.
On Error Resume Next
Die On Error
Anweisung selbst kann das Schlüsselwort Resume
, um die VBA-Laufzeitumgebung anzuweisen , alle Fehler effektiv zu ignorieren .
Wenn der Fehler zuvor nicht wirklich behandelt wird , kann die Ausführung mit möglicherweise ungültigen Daten fortgesetzt werden, was zu logischen Fehlern und unerwartetem Verhalten führen kann .
Die Betonung oben kann nicht genug betont werden. On Error Resume Next ignoriert effektiv alle Fehler und schiebt sie unter den Teppich . Ein Programm, das mit einem Laufzeitfehler bei ungültiger Eingabe explodiert, ist ein besseres Programm als eines, das mit unbekannten / unbeabsichtigten Daten weiterläuft - sei es nur, weil der Fehler viel einfacher zu identifizieren ist. On Error Resume Next
kann On Error Resume Next
leicht ausblenden .
Die On Error
Anweisung hat einen prozeduralen Geltungsbereich. Aus diesem Grund sollte es in einer bestimmten Prozedur normalerweise nur eine solche On Error
Anweisung geben.
Manchmal kann jedoch eine Fehlerbedingung nicht ganz vermieden werden, und ein Sprung in eine Fehlerbehandlungs-Subroutine, nur um Resume Next
fühlt sich einfach nicht richtig an. In diesem speziellen Fall ist die bekannte zu möglicherweise fail - Anweisung kann zwischen zwei eingewickelt werden On Error
- Anweisungen:
On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0
Die On Error GoTo 0
Anweisung setzt die Fehlerbehandlung in der aktuellen Prozedur zurück, sodass alle weiteren Anweisungen, die einen Laufzeitfehler verursachen , in dieser Prozedur nicht behandelt werden und stattdessen den Aufrufstapel übergeben, bis er von einem aktiven Fehlerhandler abgefangen wird. Wenn der Aufrufstack keinen aktiven Fehlerhandler enthält, wird er als nicht behandelte Ausnahme behandelt.
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
Benutzerdefinierte Fehler
Wenn Sie eine spezialisierte Klasse schreiben, müssen Sie häufig spezifische Fehler auslösen, und Sie möchten eine saubere Methode für Benutzer- / Aufrufcode, um diese benutzerdefinierten Fehler zu behandeln. Eine gute Möglichkeit, dies zu erreichen, ist die Definition eines speziellen Enum
Typs:
Option Explicit
Public Enum FoobarError
Err_FooWasNotBarred = vbObjectError + 1024
Err_BarNotInitialized
Err_SomethingElseHappened
End Enum
Die Verwendung der vbObjectError
Konstante vbObjectError
stellt sicher, dass die benutzerdefinierten Fehlercodes nicht mit reservierten / vorhandenen Fehlercodes überlappen. Es muss nur der erste Aufzählungswert explizit angegeben werden, da der zugrunde liegende Wert jedes Enum
Err_BarNotInitialized
um 1
größer ist als der vorherige Member, sodass der zugrunde liegende Wert von Err_BarNotInitialized
implizit vbObjectError + 1025
.
Erhöhen Sie Ihre eigenen Laufzeitfehler
Mit der Err.Raise
Anweisung kann ein Laufzeitfehler Err.Raise
werden, sodass der benutzerdefinierte Err_FooWasNotBarred
Fehler wie folgt Err_FooWasNotBarred
werden kann:
Err.Raise Err_FooWasNotBarred
Die Err.Raise
Methode kann auch benutzerdefinierte Parameter für Description
und Source
übernehmen. Aus diesem Grund ist es eine gute Idee, auch Konstanten für die Beschreibung der benutzerdefinierten Fehler zu definieren:
Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."
Erstellen Sie dann eine dedizierte private Methode, um jeden Fehler zu melden:
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
Die Klassenimplementierung kann dann einfach diese speziellen Prozeduren aufrufen, um den Fehler zu melden:
Public Sub DoSomething()
'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
'...
End Sub
Der Client-Code kann dann Err_BarNotInitialized
wie jeden anderen Fehler in seiner eigenen Fehlerbehandlungs-Subroutine behandeln.
Hinweis: Das ältere Schlüsselwort Error
kann auch anstelle von Err.Raise
ist jedoch veraltet / veraltet.