2010-12-13 12 views
3

Was ist eine gute Methode für die Fehlerbehandlung in mathematischen Funktionen? Ich baue eine Bibliothek (ein Modul) von spezialisierten Funktionen auf und mein Hauptzweck ist es, das Debugging für den Code, der diese Funktionen aufruft, zu vereinfachen - und nicht eine glänzende benutzerfreundliche Fehlerbehandlungseinrichtung zu schaffen.Fehler in mathematischen Funktionen behandeln

Unten ist ein einfaches Beispiel in VBA, aber ich bin daran interessiert, von anderen Sprachen zu hören. Ich bin nicht ganz sicher, wo ich eine Fehlermeldung/Status/Flag zurückgeben sollte. Als zusätzliches Argument?

Function AddArrays(arr1, arr2) 
    Dim i As Long 
    Dim result As Variant 

    ' Some error trapping code here, e.g. 
    ' - Are input arrays of same size? 
    ' - Are input arrays numeric? (can't add strings, objects...) 
    ' - Etc. 

    ' If no errors found, do the actual work... 
    ReDim result(LBound(arr1) To UBound(arr1)) 
    For i = LBound(arr1) To UBound(arr1) 
     result(i) = arr1(i) + arr2(i) 
    Next i 

    AddArrays = result 
End Function 

oder etwas wie folgt. Die Funktion gibt ein boolesches "success" -Flag zurück (wie im folgenden Beispiel, das False zurückgibt, wenn die Eingabearrays nicht numerisch sind usw.) oder eine Fehlernummer/Nachricht eines anderen Typs.

Function AddArrays(arr1, arr2, result) As Boolean 

    ' same code as above 

    AddArrays = booSuccess 

End Function 

Allerdings bin ich darüber nicht zu verrückt, da es die schön und gut lesbar Aufrufsyntax ruiniert, das heißt nicht c = AddArrays(a,b) sagen mehr.

Ich bin offen für Vorschläge!

Antwort

8

Offensichtlich ist die Fehlerbehandlung im Allgemeinen ein großes Thema, und was die beste Vorgehensweise ist, hängt stark von den Fähigkeiten der Sprache ab, mit der Sie arbeiten und wie die Routine, die Sie programmieren, zu anderen Routinen passt. Daher beschränke ich meine Antwort auf VBA (in Excel) und Routinen vom Typ Bibliothek, die Sie beschreiben.

Ausnahmen vs. Fehlercodes in Bibliotheksroutinen

In diesem Fall würde ich keinen Rückgabecode verwenden. VBA unterstützt eine Form der Ausnahmebehandlung, die zwar nicht so leistungsfähig ist wie das Standardformular in C++/Java/??. NET, aber ziemlich ähnlich ist. Daher gilt allgemein der Rat dieser Sprachen. Sie verwenden Ausnahmen, um aufrufenden Routinen mitzuteilen, dass die aufgerufene Routine ihren Job aus irgendeinem Grund nicht ausführen kann. Sie behandeln Ausnahmen auf der niedrigsten Ebene, auf der Sie etwas Sinnvolles zu diesem Fehler machen können.

Bjarne Stroustrup gibt eine sehr gute Erklärung, warum Ausnahmen in diesem Buch besser als Fehlercodes für diese Art von Situation sind. (Das Buch ist über C++, aber die Prinzipien hinter C++ Ausnahmebehandlung und VBA Fehlerbehandlung sind die gleichen.)

http://www2.research.att.com/~bs/3rd.html

Hier ist ein schöner Ausschnitt ist aus Abschnitt 8.3:

Wenn ein Programm besteht aus separaten Modulen, und vor allem, wenn diese Module aus separat entwickelten Bibliotheken stammen, muss die Fehlerbehandlung in zwei verschiedene Teile getrennt sein: [1] Die Meldung von Fehlerbedingungen, die kann nicht lokal gelöst werden [2] Die Behandlung von Fehlern an anderer Stelle erkannt Der Autor einer Bibliothek kann Laufzeitfehler erkennen, aber im Allgemeinen haben keine Ahnung, was Sie dagegen tun können. Der Benutzer einer Bibliothek kann wissen, wie man mit solche Fehler bewältigen kann, aber kann sie nicht entdecken - oder sie würden im Code des Benutzers gehandhabt werden und nicht verlassen, damit die Bibliothek findet.

Die Abschnitte 14.1 und 14.9 behandeln auch Ausnahmen im Vergleich zu Fehlercodes in einem Bibliothekskontext. (Es gibt eine Kopie des Buches online unter archive.org.)

Es gibt wahrscheinlich viel mehr darüber auf Stackoverflow.Ich habe gerade diese, zum Beispiel:

Exception vs. error-code vs. assert

(. Es kann eine ordnungsgemäße Verwaltung der Ressourcen Gefahren beteiligt, die bereinigt werden müssen, wenn Ausnahmen verwenden, aber sie nicht wirklich hier gelten)

Ausnahmen in VBA

Hier ist, wie eine Ausnahme Erhöhung in VBA sieht (obwohl die VBA Terminologie "eine Fehler Anhebung"):

Function AddArrays(arr1, arr2) 
    Dim i As Long 
    Dim result As Variant 

    ' Some error finding code here, e.g. 
    ' - Are input arrays of same size? 
    ' - Are input arrays numeric? (can't add strings, objects...) 
    ' - Etc. 

    'Assume errorsFound is a variable you populated above... 
    If errorsFound Then 
     Call Err.Raise(SOME_BAD_INPUT_CONSTANT) 'See help about the VBA Err object. (SOME_BAD_INPUT_CONSTANT is something you would have defined.) 
    End If 

    ' If no errors found, do the actual work... 
    ReDim result(LBound(arr1) To UBound(arr1)) 
    For i = LBound(arr1) To UBound(arr1) 
     result(i) = arr1(i) + arr2(i) 
    Next i 

    AddArrays = result 
End Function 

Wenn diese Routine den Fehler nicht abfängt, wird VBA anderen Routinen darüber im Call-Stack eine Chance geben (Siehe hierzu: VBA Error "Bubble Up"). So könnte ein Anrufer dies tun:

Was "etwas Sinnvolles" bedeutet, hängt vom Kontext Ihrer Anwendung ab. Im Fall meines oben genannten Anruferbeispiels entscheidet , dass entscheidet, dass einige Fehler behandelt werden sollen, indem ein Fehlerwert zurückgegeben wird, den Excel in eine Arbeitsblattzelle einfügen kann, während andere eine unangenehme Warnung erfordern. (Hier ist der Fall von VBA in Excel eigentlich kein schlechtes spezifisches Beispiel, weil viele Anwendungen zwischen internen und externen Routinen und zwischen Ausnahmen, mit denen Sie rechnen müssen, und Fehlerbedingungen, die Sie nur wissen wollen, unterscheiden aber für die Sie keine Antwort.)

nicht Vergessen Sie Assertions

Weil Sie das Debuggen erwähnt, es ist auch erwähnenswert, die Rolle der Behauptungen. Wenn Sie AddArrays, immer nur aufgerufen werden, indem Routinen erwarten, die tatsächlich haben ihre eigenen Arrays erstellt oder auf andere Weise überprüft sie Arrays verwenden, können Sie dies tun:

Function AddArrays(arr1, arr2) 
    Dim i As Long 
    Dim result As Variant 

    Debug.Assert IsArray(arr1) 
    Debug.Assert IsArray(arr2) 

    'rest of code... 
End Function 

Eine fantastische Diskussion über den Unterschied zwischen Behauptungen und Ausnahmen ist hier :

Debug.Assert vs Exception Throwing

ich habe hier ein Beispiel:

Is assert evil?

Einige VBA Benachrichtigung Impressum Allgemeine Array Behandlungsroutinen

schließlich als VBA-spezifische Note gibt es VBA-Varianten und Arrays sind mit einer Reihe von Gefahren, die vermieden werden müssen, wenn Sie versuchen, allgemeine Bibliothek schreiben Routinen. Arrays können mehr als eine Dimension haben, ihre Elemente können Objekte oder andere Arrays sein, ihre Start- und Endindizes können alles sein usw. Hier ist ein Beispiel (ungetestet und nicht erschöpfend), das einiges davon ausmacht:

'NOTE: This has not been tested and isn't necessarily exhaustive! It's just 
'an example! 
Function addArrays(arr1, arr2) 

    'Note use of some other library functions you might have... 
    '* isVect(v) returns True only if v is an array of one and only one 
    ' dimension 
    '* lengthOfArr(v) returns the size of an array in the first dimension 
    '* check(condition, errNum) raises an error with Err.Number = errNum if 
    ' condition is False 

    'Assert stuff that you assume your caller (which is part of your 
    'application) has already done - i.e. you assume the caller created 
    'the inputs, or has already dealt with grossly-malformed inputs 
    Debug.Assert isVect(arr1) 
    Debug.Assert isVect(arr2) 
    Debug.Assert lengthOfArr(arr1) = lengthOfArr(arr2) 
    Debug.Assert lengthOfArr(arr1) > 0 

    'Account for VBA array index flexibility hell... 

    ReDim result(1 To lengthOfArr(arr1)) As Double 
    Dim indResult As Long 

    Dim ind1 As Long 
    ind1 = LBound(arr1) 

    Dim ind2 As Long 
    ind2 = LBound(arr2) 

    Dim v1 
    Dim v2 

    For indResult = 1 To lengthOfArr(arr1) 

     'Note implicit coercion of ranges to values. Note that VBA will raise 
     'an error if an object with no default property is assigned to a 
     'variant. 
     v1 = arr1(ind1) 
     v2 = arr2(ind2) 

     'Raise errors if we have any non-numbers. (Don't count a string 
     'with numeric text as a number). 
     Call check(IsNumeric(v1) And VarType(v1) <> vbString, xlErrValue) 
     Call check(IsNumeric(v2) And VarType(v2) <> vbString, xlErrValue) 

     'Now we don't expect this to raise errors. 
     result(indResult) = v1 + v2 

     ind1 = ind1 + 1 
     ind2 = ind2 + 1 
    Next indResult 

    addArrays = result 
End Function 
+0

Wow. Ich würde das mehr als einmal abstimmen, wenn ich könnte. – RolandTumble

+0

Danke. Ich habe einen Fehler in meinem Hintern über EH in VBA, weil die Leute immer drauf los sind. Und sie vergleichen es fast immer mit Sprachen mit sehr ähnlichen (wenn auch weniger klobigen und zugegebenermaßen nicht mit "On Error Resume Next" bezeichneten) Formen der Ausnahmebehandlung. – jtolle

+0

Dies ist eine kick-ass Antwort! Vielen Dank. –

2

Es gibt viele Möglichkeiten, Fehler zu erfassen, einige besser als andere. Vieles davon hängt von der Art des Fehlers und davon ab, wie Sie damit umgehen wollen.

1st: In Ihren Beispielen behandeln Sie nicht die grundlegende Kompilierung & Laufzeitfehler (siehe Code unten).

Function Foobar (Arg1, Arg2) 
    On Error goto EH 
    Do stuff 
    Exit Function 
EH: 
    msgbox "Error" & Err.Description 
End Function 

2.: Mit dem Rahmen Beispiel oben können Sie fügen Sie alle if-then logischen Fehler Trapping-Anweisungen & es dem EH Schritt füttern wollen. Sie können sogar mehrere EH-Schritte hinzufügen, wenn Ihre Funktion komplex genug ist. Wenn Sie so vorgehen, können Sie die bestimmte Funktion finden, bei der Ihr logischer Fehler aufgetreten ist.

3.: In Ihrem letzten Beispiel ist das Beenden dieser Funktion als boolescher Wert nicht die beste Methode. Wenn Sie die 2 Arrays hinzufügen konnten, sollte diese Funktion das resultierende Array zurückgeben. Wenn nicht, sollte es einen msgbox-artigen Fehler auslösen.

Vierte: Ich habe vor kurzem angefangen, einen kleinen Trick zu machen, der in einigen Situationen sehr hilfreich sein kann. Navigieren Sie in Ihrem VBA-Editor zu Extras-> Optionen-> Allgemein-> Alle Fehler unterbrechen. Dies ist sehr hilfreich, wenn Sie bereits Ihren Fehlerbehandlungscode installiert haben, aber Sie möchten genau in die Zeile gehen, in der der Fehler aufgetreten ist, und Sie möchten keinen fehlerfreien Code löschen.

Beispiel: Nehmen wir an, Sie möchten einen Fehler abfangen, der von VBA nicht normal erfasst wird, d. H. Eine Ganzzahlvariable sollte immer einen Wert> 2 haben. Irgendwo in deinem Code, sagen wir If intvar<=2 then goto EH. Fügen Sie dann in Ihrem EH-Schritt If intvar<=2 then msgbox "Intvar=" & Intvar hinzu.

+0

Ich bin bei dir bis zu 4. Siehe meine Antwort. – RolandTumble

+0

Und # 3. Ich denke, Sie sollten den Anrufer entscheiden lassen, was mit dem Fehler zu tun ist. Vielleicht ist es ein Meldungsfeld, vielleicht gibt es einen Fehlercode zurück, vielleicht ist es nichts und es lässt den Fehler den Stapel zu einem Anrufer abwickeln, der eine Meinung hat ... – jtolle

+0

Aha ... kannte das "Break on ALL errors" nicht "Trick. Ich habe EH weggelassen, weil es das Debugging so viel komplizierter gemacht hat, aber das könnte helfen. –

3

Zuerst gab Ihnen PowerUser eine gute Antwort - das ist eine Erweiterung.

Ein Trick, dass ich gerade gelernt haben, ist die „doppelte resume“, also:

Function Foobar (Arg1, Arg2) 
On Error goto EH 
    Do stuff 

FuncExit: 
    Exit Function 
EH: 
    msgbox "Error" & Err.Description 
    Resume FuncExit 
    Resume 
End Function 

Was, dass Ihr Code eine MsgBox wirft in der Ausführung fertigen Code ist hier passiert, wenn ein Fehler ist angetroffen, dann läuft die Exit Function Anweisung & geht auf den Weg (genau wie das Herausfallen der Unterseite mit End Function).Wenn Sie jedoch das Debuggen ausführen und diese MsgBox erhalten, führen Sie stattdessen eine manuelle Ctrl-Unterbrechung aus, setzen die nächste Anweisung (Strg-F9) auf die schmucklose Resume und drücken F8, um zu gehen - es geht direkt zurück zu der Zeile, die geworfen hat der Fehler. Sie müssen nicht einmal die extra Resume Anweisungen herausnehmen, da sie niemals ausführen ohne manuellen Eingriff.

Der andere Punkt, über den ich (sanft) mit PowerUser argumentieren möchte, ist das letzte Beispiel. Ich denke, es ist am besten, unnötige GoTo Aussagen zu vermeiden. Ein besserer Ansatz ist If intvar<=2 then err.raise SomeCustomNumber. Stellen Sie sicher, dass Sie eine Nummer verwenden, die noch nicht verwendet wird - suchen Sie nach "VB benutzerdefinierter Fehler" für weitere Informationen.

+0

Guter Punkt.Ich muss meine eigenen Fehler fast nie so werfen, aber wenn ich das tue, werde ich einen benutzerdefinierten Fehler wie von Ihnen vorgeschlagen auslösen. – PowerUser

+0

Double Aha ... Ich mag den Doppel-Resume-Trick. –

+0

Netter Trick! Das hat mich schon immer gestört ... – Gaffi

Verwandte Themen