Zoeken…


Vermijden van foutcondities

Wanneer een runtime-fout optreedt, moet een goede code dit afhandelen. De beste foutafhandelingsstrategie is om code te schrijven die op foutcondities controleert en eenvoudig vermijdt dat code wordt uitgevoerd die resulteert in een runtime-fout.

Een belangrijk element bij het verminderen van runtime-fouten, is het schrijven van kleine procedures die één ding doen . Hoe minder redenen procedures hebben om te mislukken, hoe gemakkelijker de code als geheel te debuggen is.


Runtime error 91 vermijden - Object of Met blokvariabele niet ingesteld:

Deze fout wordt weergegeven wanneer een object wordt gebruikt voordat de referentie is toegewezen. Men zou een procedure kunnen hebben die een objectparameter ontvangt:

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

Als aan het target geen referentie wordt toegewezen, geeft de bovenstaande code een fout die gemakkelijk kan worden vermeden door te controleren of het object een werkelijke objectreferentie bevat:

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

Als aan het target geen referentie wordt toegewezen, wordt de niet-toegewezen referentie nooit gebruikt en treedt er geen fout op.

Deze manier om een procedure vroegtijdig te verlaten wanneer een of meer parameters niet geldig zijn, wordt een bewakingsclausule genoemd .


Runtime error 9 vermijden - Subscript buiten bereik:

Deze fout treedt op wanneer een array buiten zijn grenzen wordt gebruikt.

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

Gegeven een index die groter is dan het aantal werkbladen in het ActiveWorkbook , zal de bovenstaande code een runtime-fout veroorzaken. Een eenvoudige bewakingsbepaling kan voorkomen dat:

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

De meeste runtime-fouten kunnen worden vermeden door de waarden die we gebruiken zorgvuldig te verifiëren voordat we ze gebruiken, en dienovereenkomstig een ander uitvoeringspad te gebruiken met behulp van een eenvoudige If instructie - in bewakingsclausules die geen veronderstellingen maakt en de parameters van een procedure valideert, of zelfs in de lichaam van grotere procedures.

On Error-verklaring

Zelfs met bewakingsclausules kan men niet realistisch altijd alle mogelijke foutcondities verklaren die in de procedure kunnen worden opgeworpen. De On Error GoTo verklaring instrueert VBA te springen om een lijn label en voer "error handling mode" wanneer een onverwachte fout optreedt tijdens de uitvoering. Na het afhandelen van een fout, kan code worden hervat in "normale" uitvoering met behulp van het trefwoord Resume .

Line opschriften subroutines: omdat subroutines zijn afkomstig uit legacy BASIC code en maakt gebruik van GoTo en GoSub jumps en Return statements om terug te gaan naar de "main" routine, het is vrij eenvoudig te schrijven moeilijk te volgen spaghetti code als de dingen zijn niet strikt gestructureerd . Om deze reden is het het beste dat:

  • een procedure heeft slechts één foutafhandelingsroutine
  • de foutafhandelingssubroutine wordt alleen uitgevoerd in een foutstatus

Dit betekent dat een procedure die fouten behandelt, als volgt moet worden gestructureerd:

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

Foutafhandelingsstrategieën

Soms wilt u verschillende fouten met verschillende acties verwerken. In dat geval zult u het globale Err object inspecteren, dat informatie zal bevatten over de opgetreden fout - en dienovereenkomstig te handelen:

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

Over het algemeen kunt u overwegen de foutafhandeling voor de volledige subroutine of functie in te schakelen en alle fouten af te handelen die zich binnen de scope kunnen voordoen. Als u alleen fouten in het kleine gedeelte van de code moet afhandelen, schakelt u foutafhandeling op hetzelfde niveau in en uit:

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

Lijn nummers

VBA ondersteunt regelnummers in oude stijl (bijv. QBASIC). De verborgen Erl eigenschap kan worden gebruikt om het regelnummer te identificeren dat de laatste fout heeft veroorzaakt. Als u geen regelnummers gebruikt, retourneert Erl alleen 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

Als u gebruik maakt van lijn nummers, maar niet consequent, dan Erl zal de laatste regel nummer te retourneren, voordat de instructie die de fout opgeheven.

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

Houd er rekening mee dat Erl ook alleen Integer precisie heeft en stilletjes overloopt. Dit betekent dat regelnummers buiten het gehele getal onjuiste resultaten geven:

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

Het regelnummer is niet zo relevant als de verklaring die de fout heeft veroorzaakt, en nummeringsregels worden snel vervelend en niet helemaal onderhoudsvriendelijk.

Hervat trefwoord

Een subroutine voor foutafhandeling zal:

  • uitvoeren tot het einde van de procedure, in welk geval de uitvoering wordt hervat in de aanroepprocedure.
  • of gebruik het trefwoord Resume om de uitvoering binnen dezelfde procedure te hervatten .

Het sleutelwoord Resume mag alleen worden gebruikt in een subroutine voor foutafhandeling, want als VBA Resume tegenkomt zonder zich in een foutstatus te bevinden, wordt runtime error 20 "Resume without error" opgeworpen.

Er zijn verschillende manieren waarop een foutafhandelingssubroutine het trefwoord Resume kan gebruiken:

  • Resume alleen gebruikt, uitvoering wordt voortgezet op de verklaring die de fout heeft veroorzaakt . Als de fout niet daadwerkelijk wordt behandeld voordat u dat doet, wordt dezelfde fout opnieuw opgeworpen en kan de uitvoering een oneindige lus binnentreden.
  • Resume Next gaat door met het uitvoeren van de instructie onmiddellijk na de instructie die de fout heeft veroorzaakt. Als de fout niet daadwerkelijk wordt behandeld voordat u dat doet, mag de uitvoering worden voortgezet met mogelijk ongeldige gegevens, wat kan leiden tot logische fouten en onverwacht gedrag.
  • Resume [line label] gaat door met de uitvoering op het opgegeven lijnlabel (of lijnnummer, als u oudere lijnnummers gebruikt). Dit maakt doorgaans het mogelijk om wat opschoningscode uit te voeren voordat de procedure netjes wordt afgesloten, zoals ervoor zorgen dat een databaseverbinding wordt gesloten voordat deze terugkeert naar de beller.

On Error Resume Next

De On Error instructie zelf kan het sleutelwoord Resume om de VBA-runtime te instrueren om effectief alle fouten te negeren .

Als de fout niet daadwerkelijk wordt behandeld voordat u dat doet, mag de uitvoering worden voortgezet met mogelijk ongeldige gegevens, wat kan leiden tot logische fouten en onverwacht gedrag .

Bovenstaande nadruk kan niet genoeg worden benadrukt. On Error Resume Next negeert effectief alle fouten en schuift ze onder het tapijt . Een programma dat wordt opgeblazen met een runtime-fout bij ongeldige invoer, is een beter programma dan een programma dat blijft draaien met onbekende / onbedoelde gegevens - zij het alleen omdat de bug veel gemakkelijker kan worden geïdentificeerd. On Error Resume Next kan gemakkelijk bugs verbergen .

De On Error instructie is procedure-scoped - dat is waarom er normaal slechts één , enkele On Error instructie in een bepaalde procedure zou moeten zijn.

Soms kan een foutconditie echter niet helemaal worden vermeden en voelt het niet goed om naar een subroutine voor foutafhandeling te springen om alleen Resume Next te Resume Next . In dit specifieke geval kan de bekende-mogelijk-falen-instructie worden ingepakt tussen twee On Error instructies:

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

De instructie On Error GoTo 0 reset de foutafhandeling in de huidige procedure, zodat elke verdere instructie die een runtime-fout veroorzaakt, binnen die procedure niet wordt verwerkt en in plaats daarvan de call-stack doorgeeft totdat deze wordt opgevangen door een actieve foutafhandelaar. Als er geen actieve foutafhandelaar in de oproepstack staat, wordt deze behandeld als een onverwerkte uitzondering.

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

Aangepaste fouten

Vaak wilt u bij het schrijven van een gespecialiseerde klasse dat deze zijn eigen specifieke fouten oproept, en wilt u een schone manier voor gebruiker / aanroepcode om met deze aangepaste fouten om te gaan. Een nette manier om dit te bereiken is door een specifiek Enum type te definiëren:

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

Het gebruik van de ingebouwde constante vbObjectError zorgt ervoor dat de aangepaste foutcodes niet overlappen met gereserveerde / bestaande foutcodes. Alleen de eerste enum-waarde moet expliciet worden opgegeven, want de onderliggende waarde van elk Enum lid is 1 groter dan het vorige lid, dus de onderliggende waarde van Err_BarNotInitialized is impliciet vbObjectError + 1025 .

Uw eigen runtime-fouten verhogen

Een runtime-fout kan worden verhoogd met behulp van de Err.Raise instructie, dus de aangepaste Err_FooWasNotBarred fout kan als volgt worden opgeworpen:

Err.Raise Err_FooWasNotBarred

De Err.Raise methode kunnen ook op maat Description en Source parameters - om deze reden is het een goed idee om ook constanten definiëren om vast te houden beschrijving elke aangepaste foutpagina's:

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

En maak vervolgens een speciale privémethode om elke fout te 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

De implementatie van de klasse kan dan eenvoudigweg deze gespecialiseerde procedures aanroepen om de fout te veroorzaken:

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

De clientcode kan dan Err_BarNotInitialized verwerken zoals elke andere fout binnen de eigen subroutine voor foutafhandeling.


Opmerking: het oude trefwoord Error kan ook worden gebruikt in plaats van Err.Raise , maar het is verouderd / verouderd.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow