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.