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 den Resume 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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow