|
Siehe auch ExceptionsDiskussion, ExceptionsAlsArchitekturBestandteil, EinGutesExceptionBeispiel, ExceptionsConsideredHarmful. |
|
Siehe auch ExceptionsDiskussion, ExceptionsAlsArchitekturBestandteil, EinGutesExceptionBeispiel, ExceptionsConsideredHarmful, CheckedExceptionsConsideredHarmful. |
|
Primäres Ziel eines in der Sprache und dem Laufzeitsystem verankerten Exception-Handling? ist es daher, zuverlässig zu verhindern, dass gescheiterte Methodenaufrufe (= "Vertragsbrüche") vom Aufrufer versehentlich ignoriert werden und deren Ergebnisse trotzdem benutzt werden. Zugleich und eine gröbere Fehlerbehandlung auf höherer Ebene zu ermöglichen, statt dass man nach den meisten Function-Calls? einen Return-Code? auswerten muss: |
|
Primäres Ziel eines in der Sprache und dem Laufzeitsystem verankerten Exception-Handling? ist es daher, zuverlässig zu verhindern, dass gescheiterte Methodenaufrufe (= "Vertragsbrüche") vom Aufrufer versehentlich ignoriert werden und deren Ergebnisse trotzdem benutzt werden. Zugleich ermöglicht Exception-Handling? eine gröbere Fehlerbehandlung auf höherer Ebene, statt dass man nach den meisten Function-Calls? einen Return-Code? auswerten muss: |
|
Exception-Handling? ist eine Methode, "Vertragsbrüche" nicht-ignorierbar zu machen und ihre Behandlung ohne viel Umstand von der unterst-möglichen auf die am besten geeignete Behandlungsebene zu verschieben. |
|
Exception-Handling? ist eine Methode, "Vertragsbrüche" beim Methodenaufruf nicht-ignorierbar zu machen und ihre Behandlung ohne viel Umstand von der unterst-möglichen auf die am besten geeignete Behandlungsebene zu verschieben. |
|
Gegenmaßnahmen in Lava: |
|
Maßnahmen in Lava: |
|
# Wenn eine Methode in einem Zweig nicht alle Output-Parameter? erfolgreich setzen kann, so kann dieser Zweig nur mit einem "fail <expression>"-Statement abgeschlossen werden, das eine Exception auslöst: wird zur Programmierzeit kontrolliert; der Wert von <expression> definiert das zugehörige Exception-Objekt?. Sein Typ muss durch eine der Klassen beschrieben werden, die in der "throws"-Klausel der Methoden-Deklaration? enthalten sind. |
|
# Wenn eine Methode in einem Zweig nicht alle Output-Parameter? erfolgreich setzen kann, so kann dieser Zweig nur mit einem "fail <expression>"-Statement abgeschlossen werden, das eine Exception auslöst: wird zur Programmierzeit erzwungen; der Wert von <expression> definiert das zugehörige Exception-Objekt?. Sein Typ muss durch eine der Klassen beschrieben werden, die in der "throws"-Klausel ("Exception-Spezifikation?") der Methoden-Deklaration? enthalten sind. Anders als in Java muss die Exception-Spezifikation? jedoch keine Exceptions auflisten, die von untergeordneten Methodenaufrufen herrühren können. Vgl. CheckedExceptionsConsideredHarmful. |
|
Es gibt keine "catch-Klauseln" in Lava: In Lava wird das "Abfangen" von Exceptions (try) nicht zwangsweise mit dem Verzweigen über Exception-Klassen? (catch) verbunden. Vielmehr kann man jedes Statement durch einen oder mehrere Exception-Handling-Zweige? ergänzen, die mit "or if exception:" beginnen. (Hintere Zweige werden nur benutzt, wenn vordere Zweige wiederum Exceptions auslösen.) |
|
# Lava unterstützt |
|
[[Code] try <statement> or if exception: <exception_handling1> or if exception: <exception_handling2> ... ] Diese Abfolge von "or if exception"-Zweigen soll zugleich verdeutlichen, dass es beim Exception-Handling? um das planmäßige Durchspielen von Alternativen geht, falls die normale Vorgehensweise oder vorrangige Alternativen misslingen. Beim Betreten eines solchen Zweiges wird die aktuelle Exception "abgeschaltet", ist von da an also nicht mehr wirksam. Mit der "Exception::Last"-Funktion der Basisklasse "Exception" aller Exceptions kann aber das zuletzt geworfene Exception-Objekt? immer noch beschafft werden. Letzteres kann dann in diesem Zweig entweder für eine objekt-orientierte, oder für eine klassische (= typ-verzweigende) Exception-Behandlung? benutzt werden: # Objekt-orientiertes Exception-Handling?: Wenn die betreffende Exception-Klasse? von demselben Programmierer definiert worden ist, der auch die Behandlung einer Exception dieser Klasse programmiert, so kann er für diese Klasse eine Überschreibung der Methode "Catch" der Klasse "Object" vornehmen und einfach Exception::Last.Catch aufrufen. Je nach der Laufzeit-Klasse? des aktuellen Exception-Objektes? wird dann eine spezifische Catch-Methode? aufgerufen. (Object::Catch gibt einfach den aktuellen Call-Stack? zum Zeitpunkt der Exception, sowie einen Fehlercode und eine textuelle Fehlermeltung aus, die Bestandteil jedes Exception-Objektes? sind.) # Klassisches, typ-verzweigendes Exception-Handling?: Wenn der Exception-Handling-Programmierer? die Klasse der abzufangenden Exception nicht selbst gestalten kann, so kann er das "Type-Switch-Statement?" von Lava benutzen, um über den Laufzeit-Typ? von Exception::Last zu verzweigen und bestimmte Arten von Exceptions einer spezifischen Behandlung zuzuführen. [[Code] try <statement> or if exception: type of Exception::Last case <type1> exc1: <exception_handling1> // wertet Exception-Objekt? exc1 aus case <type2> exc2: <exception_handling2> // wertet Exception-Objekt? exc2 aus ... else ... #type or if exception: ... ] |
|
Neben dem klassischen "typverzweigenden" Exception-Handling? mittels "catch"-Klauseln ermöglicht Lava auch ein mehr objektorientiertes Exception-Handling? durch Überschreibung der Methode "Catch" der Klasse "Object": Man benutzt in der "catch"-Klausel lediglich die unspezifische gemeinsame Basisklasse "Exception" aller Exceptions und ruft "Catch" mit dem entsprechenden Exceptionobjekt auf. Object::Catch gibt einfach den aktuellen Call-Stack? zum Zeitpunkt der Exception, sowie einen Fehlercode und eine textuelle Fehlermeltung aus, die Bestandteil jedes Exception-Objektes? sind. |
|
Hier könnte man natürlich das Verzweigen über den Exception-Typ? auch in eine Function verlegen, die dann, ebenso wie in dem Catch-Fall?, und anders als bei try...catch, an mehreren Stellen zur Exception-Behandlung? benutzt werden könnte. |
|
Auf eine "Aufräum"-Klausel "finally" (Java, C#), die unter allen Umständen bei Verlassen des try-Statements ausgeführt wird, haben wir in Lava verzichtet: Generell wird die Lebensdauer von Lava-Objekten? durch Referenzzähler kontrolliert. Dadurch ist gesichert, dass ein Objekt immer zum frühestmöglichen Zeitpunkt automatisch freigegeben wird, nämlich sobald es nicht mehr referiert wird. |
|
Automatische Aufräum-Maßnahmen?: Generell wird die Lebensdauer von Lava-Objekten? durch Referenzzähler kontrolliert. Dadurch ist gesichert, dass ein Objekt immer zum frühestmöglichen Zeitpunkt freigegeben wird, nämlich sobald es nicht mehr referiert wird. Zu diesem wohldefinierten Zeitpunkt wird dann auch automatisch die Finalize-Methode? aufgerufen (die entweder von Klasse "Object" geerbt wird oder handprogrammiert sein kann). Durch expliziten Aufruf von Objekt::Finalize kann ein Objekt vorzeitig unbenutzbar gemacht ("zombifiziert") werden: Jeder Benutzungsversuch löst dann eine spezifische Exception aus. |
|
Zu diesem wohldefinierten Zeitpunkt wird dann auch automatisch die Finalize-Methode? aufgerufen (die entweder von Klasse "Object" geerbt wird oder handprogrammiert sein kann). Durch expliziten Aufruf von Objekt::Finalize kann ein Objekt vorzeitig unbenutzbar gemacht ("zombifiziert") werden: Jeder nachfolgende Benutzungsversuch löst dann eine spezifische Exception aus. |
|
Ergänzende Anmerkungen: Das versehentliche Benutzen noch undefinierter Variablen-Werte? wird übrigens in Lava nicht nur für Output-Parameter?, sondern auch für lokale und Member-Variablen? (durch statische Checks) verhindert: |
|
Ergänzende Anmerkungen: Das versehentliche Benutzen undefinierter Variablen-Werte? (und damit die Haupt-Ursache? von Exceptions) wird übrigens in Lava nicht nur für Output-Parameter?, sondern auch für lokale und Member-Variablen? (durch Programmierzeit-Checks?) verhindert: |
|
# Die (lesende) Benutzung von Variablen und Ausdrücken, deren Wert optional ist (also auch null sein kann) an Stellen, wo ein nicht-optionaler Ausdruck erwartet wird, wird durch statische Checks verhindert. Man wird so gezwungen, explizit den Lava-"else"-Ausdruck oder die "ifdef"-Anweisung zu benutzen, um sicherzustellen, dass man nicht versehentlich über undefinierte optionale Ausdrucks-Werte? "stolpert". |
|
Wenn ein Input-Parameter? einer Methode in einem gewissen Wertebereich liegen muss (was beim Ariane5Absturz meines Wissens der Fall war), so müsste ein gewissenhafter Methoden-Programmierer? dies entweder durch eine Assertion ("assert"-Statement in Lava, löst bei Verletzung Exception aus) oder durch eine if-Verzweigung absichern, die nötigenfalls eine spezifische Exception auslöst. Vor allem aber muss eine solche Vorbedingung als Kommentar in der Methoden-Deklaration? ausdrücklich genannt werden, damit man schon zur Programmierzeit auf das Problem aufmerksam wird. |
|
Wenn ein Input-Parameter? einer Methode in einem gewissen Wertebereich liegen muss (was beim Ariane5Absturz meines Wissens der Fall war), so müsste ein gewissenhafter Methoden-Programmierer? dies entweder durch eine Assertion (eine Methoden-Vorbedingung?, oder ein Lava-"assert"-Statement, löst bei Verletzung Exception aus) oder durch eine if-Verzweigung absichern, die nötigenfalls eine spezifische Exception auslöst. Zumindest aber sollte eine solche Vorbedingung als Kommentar in der Methoden-Deklaration? ausdrücklich genannt werden, damit man schon zur Programmierzeit auf das Problem aufmerksam wird. |
|
Zu Exceptions siehe auch |
|
Zu Exceptions in SpracheLava siehe auch Exception-Handling? durch Callbacks: Das "typverzweigende" Exception-Handling? durch typspezifische "catch"-Klauseln ist ja eigentlich unbefriedigend und unerwünscht, weil es das objektorientierte Denken untergräbt. Das objektorientierte Exception-Handling? durch eine überschriebene "Object::catch"-Methode (s.o.) ist nur dann sinnvoll anwendbar, wenn derjenige, der die Exception behandelt, zugleich die abgeleitete Exceptionklasse "in der Hand hat", also nach seinen Vorstellungenn gestalten kann, insbesondere die überschriebene "catch"-Methode selbst programmieren kann, was meistens nicht der Fall sein wird, da die Exceptionklasse normalerweise von dem Programmierer der gerufenen Methode gestaltet werden wird. Ein angemessener objektorientierter Umgang mit diesen Fällen kann in Lava dadurch erreicht werden, dass die exception-auslösende Methode unmittelbar vor dem "Werfen" der Exception ein spezifisches "Signal" im Sinne des |
Siehe auch ExceptionsDiskussion, ExceptionsAlsArchitekturBestandteil, EinGutesExceptionBeispiel, ExceptionsConsideredHarmful, CheckedExceptionsConsideredHarmful.
Aus Sicht von SpracheLava ist ein Exception-Handling-Konzept? nicht nur sinnvoll, sondern geradezu unentbehrlich, da wir in SpracheLava generell anstreben, das versehentliche Arbeiten mit undefinierten Variablen-Werten? durch statische oder Laufzeit-Checks? zu verhindern, denn die Aufklärung solcher Fehler ist oft außerordentlich mühsam und zeitraubend:
Es kommt häufig vor, dass eine Methode einer Klasse nicht die normalerweise erwarteten Ergebnisse (Output-Parameter?, Return-Value?) liefern kann. Wenn in diesem Fall das Laufzeit-System? nicht verhindert, dass der Aufrufer diese Rückgabewerte (soweit sie von der Methode überhaupt schon vor dem Zeitpunkt der Exception gesetzt worden sind!) dennoch unbesehen benutzt, werden oft schwer aufzuklärende Folgefehler auftreten.
|
Exception-Handling? ist eine Methode, "Vertragsbrüche" beim Methodenaufruf nicht-ignorierbar zu machen und ihre Behandlung ohne viel Umstand von der unterst-möglichen auf die am besten geeignete Behandlungsebene zu verschieben.
Maßnahmen in Lava:
Auf eine "Aufräum"-Klausel "finally" (Java, C#), die unter allen Umständen bei Verlassen des try-Statements ausgeführt wird, haben wir in Lava verzichtet: Generell wird die Lebensdauer von Lava-Objekten? durch Referenzzähler kontrolliert. Dadurch ist gesichert, dass ein Objekt immer zum frühestmöglichen Zeitpunkt automatisch freigegeben wird, nämlich sobald es nicht mehr referiert wird.
Zu diesem wohldefinierten Zeitpunkt wird dann auch automatisch die Finalize-Methode? aufgerufen (die entweder von Klasse "Object" geerbt wird oder handprogrammiert sein kann). Durch expliziten Aufruf von Objekt::Finalize kann ein Objekt vorzeitig unbenutzbar gemacht ("zombifiziert") werden: Jeder nachfolgende Benutzungsversuch löst dann eine spezifische Exception aus.
Ein handprogrammiertes Finalize wird man in Lava nur benötigen, wenn es um die korrekte Freigabe von Betriebssystem- oder anderen Nicht-Lava-Objekten? geht, deren Lebensdauer nicht durch Lava-Referenzzähler? kontrolliert wird.
Ergänzende Anmerkungen: Das versehentliche Benutzen undefinierter Variablen-Werte? (und damit die Haupt-Ursache? von Exceptions) wird übrigens in Lava nicht nur für Output-Parameter?, sondern auch für lokale und Member-Variablen? (durch Programmierzeit-Checks?) verhindert:
Siehe dazu auch
http://lavape.sourceforge.net/doc/html/RepetComputSamples.htm.
Andere denkbare Maßnahmen, um das irrtümliche Arbeiten mit undefinierten oder semantisch nicht adäquaten Variablen-Werten? unwahrscheinlicher zu machen, die z. B. die Ursache für gewisse gescheiterte Weltraum-Missionen? waren:
Wenn ein Input-Parameter? einer Methode in einem gewissen Wertebereich liegen muss (was beim Ariane5Absturz meines Wissens der Fall war), so müsste ein gewissenhafter Methoden-Programmierer? dies entweder durch eine Assertion (eine Methoden-Vorbedingung?, oder ein Lava-"assert"-Statement, löst bei Verletzung Exception aus) oder durch eine if-Verzweigung absichern, die nötigenfalls eine spezifische Exception auslöst. Zumindest aber sollte eine solche Vorbedingung als Kommentar in der Methoden-Deklaration? ausdrücklich genannt werden, damit man schon zur Programmierzeit auf das Problem aufmerksam wird.
Eine amerikanische Mars-Mission? ist meines Wissens gescheitert, weil Maßeinheiten (yard und meter o. ä.) verwechselt worden waren. Dies könnte man vermeiden, indem man für Zahlen-Parameter? nicht einfach Float-, Double- oder Integer-Typen? benutzt, sondern (sofern diese Basis-Typen?, wie in Lava, normale Klassen sind) daraus abgeleitete Klassen, die die Maßeinheit ausdrücken. Dann wäre die Verwechslung schon zur Programmierzeit als Typ-Fehler? aufgefallen.
Man braucht dann aber auch ein spezifisches Ausdrucksmittel (wie die "scale"-Expressions in Lava), um einen gewöhnlichen Zahlenwert, der vielleicht aus einer mathematischen Funktion (Sinus o. ä.) herauskommt, in einen solchen "dimensionierten" Wert umzuinterpretieren/zu spezialisieren. Siehe dazu Punkt 8 der Release Notes zu Lava 0.6:
http://lavape.sourceforge.net/doc/html/ReleaseNotes.htm#R0_6, und das zugehörige Beispiel
http://lavape.sourceforge.net/doc/Samples/ScaleUnits.htm.
Zu Exceptions in SpracheLava siehe auch
http://lavape.sourceforge.net/doc/html/ExceptionSamples.htm
Exception-Handling? durch Callbacks:
Das "typverzweigende" Exception-Handling? durch typspezifische "catch"-Klauseln ist ja eigentlich unbefriedigend und unerwünscht, weil es das objektorientierte Denken untergräbt.
Das objektorientierte Exception-Handling? durch eine überschriebene "Object::catch"-Methode (s.o.) ist nur dann sinnvoll anwendbar, wenn derjenige, der die Exception behandelt, zugleich die abgeleitete Exceptionklasse "in der Hand hat", also nach seinen Vorstellungenn gestalten kann, insbesondere die überschriebene "catch"-Methode selbst programmieren kann, was meistens nicht der Fall sein wird, da die Exceptionklasse normalerweise von dem Programmierer der gerufenen Methode gestaltet werden wird.
Ein angemessener objektorientierter Umgang mit diesen Fällen kann in Lava dadurch erreicht werden, dass die exception-auslösende Methode unmittelbar vor dem "Werfen" der Exception ein spezifisches "Signal" im Sinne des
Lava-Callback-Konzeptes emittiert, für das der Methoden-Aufrufer? eine von ihm programmierte Callback-Methode? abonniert hat, die dann also vor dem formellen Auslösen der Exception auf Grund des emittierten Signals automatisch aufgerufen wird und die eigentliche Exceptionbehandlung übernimmt.
--KlausGünther