![]() |
Exception Handling.
Ich habe mir gerade mal etwas Gedanken über Exceptions gemacht und wie man einen Code wasserdicht bekommt. Dies ist mein Ergebnis:
Delphi-Quellcode:
Jetzt frage ich mich, das ist doch der blanke Wahnsinn: Drei verschachtelte try-Blöcke!
procedure TForm1.Button1Click(Sender: TObject);
const TEXT = 'Dies ist ein Testtext.'; FILENAME = 'c:\test.txt'; var fs: TFileStream; s: String; begin s := TEXT; try {1} fs := TFileStream.Create(FILENAME, fmCreate); try {2} try {3} fs.WriteBuffer(Pointer(s)^, length(s)); except on E: EWriteError do ShowMessage(E.Message); end; finally FreeAndNil(fs); end; except on E: Exception do ShowMessage(E.Message); end; end; {1}: Um die Exception abzufangen, wenn die Methode Create von TFileStream fehlschlägt. Hier bringe ich zwar auch nur die Fehlermeldung, die die Exception selber bringen würde, aber eventuell will man ja noch was anders machen. {2}: Der obligatorische Ressourcenschutzblock. {3}: Um die Exception abzufangen, die WriteBuffer auslöst, wenn was schief geht beim Schreiben. Ist das denn normal oder übertreibe ich hier? Wie gesagt, meine ShowMessages steh da nur für alternativen Code. Das heißt will man anstatt den ShowMessages noch was anderes machen, dann müssen drei try-Blöcke sein - oder? |
Re: Exception Handling.
Delphi-Quellcode:
Dürfte das gleiche machen. Im Grunde sollte man pro Procedure mit einem try finally und einem try except auskommen können.
TEXT = 'Dies ist ein Testtext.';
FILENAME = 'c:\test.txt'; var fs: TFileStream; s: String; begin s := TEXT; try fs := TFileStream.Create(FILENAME, fmCreate); try fs.WriteBuffer(Pointer(s)^, length(s)); finally FreeAndNil(fs); end; except on E: EWriteError do ShowMessage(E.Message) else on E: Exception do ShowMessage(E.Message); end; end; Gruß Hagen |
Re: Exception Handling.
Hm. Gut. Sollte klappen.
Machen wir mal etwas weiter. Folgender Code:
Delphi-Quellcode:
Einfaches Beispiel, aber man sieht deutlich eine Funktion die True oder False zurück gibt hätte es auch getan. Wo liegt konkret der Vorteil von Exceptions? Oder anders gefragt, wann ist das werfen von Exceptions sinnvoll?
type
EInvalidPW = class(Exception); const PASSWORD = 'test'; procedure CheckPWD(Pwd: String); resourcestring rsInvalidPw = 'Ungültiges Passwort'; begin if Pwd <> PASSWORD then raise EInvalidPW.Create(rsInvalidPw); end; procedure TForm1.Button1Click(Sender: TObject); begin try CheckPWD('tset'); except on E: EInvalidPW do ShowMessage(E.Message); end; end; |
Re: Exception Handling.
Ganz genau das wollte ich mal meine Profs fragen. Der einzige Grund, den ich mir vorstellen kann, ist dass wenn eine Ausnahmesituation NICHT vom Programmierer behandelt wird, so stürzt der Rechner zumindest nicht ab, sondern es wird statt dessen eine unbehandelte Exception ausgelöst und angezeigt.
Wenn man aber sauber programmiert, dann kann man sich doch aber den ganzen (wie ich finde schwachsinnigen) Overhead sparen, gell!? OOP hin oder her... FEHLER müssen doch nicht unbedingt AUCH Objekte sein :roll: Finde Exceptions im wesentlichen genau so birnig wie das Geheimnisprinzip. Was soll ich erst einen Methodcall auslösen, wenn ich doch DIREKT und SCHNELL zugreifen könnte!? Das ist in meinen Augen völlig unnötige "Theorisierung" - zwanghaftes Anpassen an ein theroetisches Konzept. OOP ist SUPER! Keine Frage. Aber man muss es doch nicht auf Biegen und Brechen bis in alle Nieschen reinzwängen wollen... Danke Luckie! Bin ich doch nicht ganz so alleine mit meinem Gefühl :) gruss, dizzy |
Re: Exception Handling.
Nun ja, ich habe da eigentlich kein Gefühl, da ich weniger mit dem Bauch als mehr mit meinem Kopf - zu mindest versuche ich es - programmiere, deswegen frage ich hier. ;)
|
Re: Exception Handling.
Ähnliches passiert mir auch öfter ... irgendwann bin ich dann dazu übergegangen machen functions selbst zu schreiben, und keine 'Unhandled Exception' auszulösen, sondern die Fehlerbehandlung vor Ort durchzuführen.
Am beispiel von StrToInt:
Delphi-Quellcode:
Wenn die (ausgegebene) Variable E true ist, dann gab es keinen Fehler, wenn nicht, gab es einen. So hat man zwar ne if-Abfrage mehr, aber man könnte so mehrere konvertierungen zusammennehmen, und dann checken, ob es irgendwo nen fehler gab.
function StrToInt_(const S: string; out E : boolean) : integer;
var x : integer; begin Val(S, result, x); E := x <> 0; end; Diese Methode ist zwar umständlich, aber manchmal hilfreich. ciao, Philipp |
Re: Exception Handling.
Zitat:
oder du benutzt die eingebaute Funktion TryStrToInt von Delphi ;) mfG mirage228 |
Re: Exception Handling.
Ist die neu? Oder meinst du die, an die ich denke: "StrToIntDef".
|
Re: Exception Handling.
Zitat:
ja, anscheinend ist die neu in Delphi 6 (oder erst 7?).
Code:
Edit: Verflixte Tags :twisted:
function TryStrToInt(const S: string; out Value: Integer): Boolean;
Beschreibung TryStrToInt konvertiert den String S, der ein Integer repräsentiert (in dezimaler oder hexadezimaler Form), in eine Zahl und weist diese Value zu. Wenn S keine gültige Zahl enthält, gibt TryStrToInt false zurück; ansonsten true. mfG mirage228 |
Re: Exception Handling.
Könnten wir bitte beim Thema bleiben? Danke.
@Hagen: Was mir gerade bei deinem Code einfällt: Wenn das Schreiben eine Exception auslöst, wird dann auch das Objekt wieder freigegeben? Um das nämlich sicher zustellen, habe ich es nämlich so geschachtelt. |
Re: Exception Handling.
1. Wir sind immer noch beim Thema, denn
Zitat:
2. wird man doch noch mal fragen dürfen. |
Re: Exception Handling.
Leider findet man über das Exception-Handling tatsächlich nur wenig konzeptionelle Literatur.
Exceptions = Ausnahmen beziehen sich nicht ausschließlicher Weise nur auf eine Fehlerbehandlung. Dies zeigte besonders der Fall mit EConnectionClosedGracefully der Indy Komponenten. Diese Exception ist eine reine Ausnahme und kein Fehler. Logisch gesehen also absolut korrekt aber für viele Programmierer eher unlogisch im Programablauf. Denoch haben die Programmierer von Indy exakt richtig gehandelt wenn man das Verhalten und die Auswirkungen einer Exception betrachtet. Die wichtigste Auswirkung einer Exception ist das sofortige Abbrechen der auslösenden Funktion und sogar der übergeordneten aufrufenden Funktionen. In den meisten VCL basierten Programmen springt der Programablauf bis zum obersten Message-Handler. Diese Wirkweise ist eine Wirkung mit sehr harten Konsequenzen im Programablauf und bedingt auch die Schaffung von technischen Maßnahmen bestimmte Codebereich denoch ausführen zu lassen. Somit kommt man sofort und logischersweise von der Möglichkeit Exceptions auslösen zu können, zu der Notwendigkeit mit try finally Blöcken diese Logik kanalisieren zu können. Konkret heist dies, arbeitet man mit Exceptions so muß man auch mit try finally's arbeiten. Da ein Coder der auf fremden Code aufbaut nie wissen kann ob Exceptions ausgelösst werden, muß der Coder nun immer mit try finally's arbeiten. Hat man nun die Konsequenzen des Exception Handlings verstanden so erkennt man sehr schnell das die generelle Anwendung von Exceptions in allen Funktionen zu einer Idiotie ausarten kann. Man muß also die gute Balance finden in wie weit man Exceptions anwendet, sprich die Verhältnissmäßigkeit der Wahl der Mittel. Dazu kann man einige Pi*Daumen Regeln anwenden: Man teilt den Programfluß dreistufig in einfache Lowlevel Funktionen, komplexere Highlevel Funktionen und GUI Funktionen auf. Normalerweise rufen GUI Funktionen wie Events, Messages nur die komplexeren Highlevel Funktionen auf. Diese wiederum die Lowlevel Funktionen. Nun, die Lowlevel Funktionen sollten Exception-frei sein, ausgenommen es werden deren Parameter überprüft (wo aber Assertions besser geeignet wären). Die High-Level Funktionen sollten mit Exceptions arbeiten da deren Programmierung von aussagekräftigen Funktionsrückgabewerten viel zu kompliziert werden kann. D.h. die Exceptions würden innerhalb dieser Highlevel Funktionen deren Aufbau wesentlich erleichtern. Die Programmierung von Rückgabewerten, wie zB. HResult bei Interfaces ist dagegen viel viel mehr Code Overhead und erhöht den Aufwand in der Codierung im Vergleich zu Exceptions. Im Grunde sind Exceptions nichts anderes als Harte Exits mit einem aussagekräftigem Grund für den Exit. Wenn EXIT die Antwort und technische Krücke der Prozeduralen Programmierung ist, dann sind die Exception die Antwort auf diese EXIT's aus Sicht der OOP. Soweit so klar. Das was nun das Auslösen von Exceptions so kompliziert macht ist deren gezielte Abarbeitung, d.h. also die Anwendung von try except end Blöcken. Die Sinnhaftigkeit von Exception bleibt nur gewahrt wenn man nicht permanent in High/Lowlevel Funktionen leere try except Blöcke codiert. D.h. die Anwendung von try except Blöcken sollte ganz genau überlegt werden und wenn angewendet dann müssen sie ganz gezielt nur auf einige Exception Typen reagieren. WARUM ?? Das Ziel des Exceptions Handlings sollte es sein für den Programmierer aussagekräftige Ausnahmebedingung vom normalen Programmfluß zu visualisieren. Werden die try except Blöcke unüberlegt angewendet so heist dies das man dieses Ziel nicht erreichen kann, der Vorteil der Exceptions geht also verloren !! Also in kurz: Exception's nur in High-Level Funktionen benutzen, die die Programflußebene darstellen die durch die GUI Events aufgerufen werden. Exceptions in Lowlevel Funktionen, wie zB. StrToInt() sind im Grunde eher störend und sollten generell vermieden werden. Im Falle von StrToInt() könnte man argumentieren das es die Highlevel Schnittstelle zu TryStrToInt() oder StrToIntDef() darstellt. Dies ist insofern richtig wenn man StrToInt() innerhalb von GUI Events betrachtet. Da StrToInt() aber eine Funktion der RTL ist würde ich eher meinen das es eine LowLevel Funktion darstellt und eigentlich KEINE Exception auslösen sollte. Exceptions NIEMALS in Release-Funktionen benutzen. Sprich Funktionen die Objecte oder Daten freigeben sollten keine Exceptions auslössen. Dies liegt daran das solche Funktionen meistens gruppiert in übergeordenten Funktionen auftreten. Würde eine Exception ausgelösst so bedeutete dies das nachfolgende Release-Funtionen Speicherleaks produzieren würden. Technologisch müsste der anwendende Coder nun JEDEN Aufruf solcher Release-Funktionen per separatem try finally schützen. Es ensteht eine Sinnlosigkeit der Exceptions in diesem Falle. Exception dagegen in Allocator-Funktionen, sprich Constructoren, verwenden. Die Exceptions verhindern somit die weitere Allokation von sinnlosen Speicherbereichen. Code immer schützen mit try finally end Blöcken, egal ob mit oder ohne Exceptions gearbeitet wird. try finally Blöcke werden IMMER ausgeführt wenn sie als ÄUSSERTER Block verwendet werden. Demzufolge sollten try finally Blöcke zu-äusserst stehen ;) In fast allen Fällen reicht EIN einzigster try finally Block pro Funktion aus. Zb. so
Delphi-Quellcode:
Hier sieht man das man immer mit einem try finally auskommen kann.
var
S,D: TStream; begin S := nil; D := nil; try S := TFileStream.Create(...); D := TFileStream.Create(...); ..... finally S.Free; D.Free; end; end; try except Blöcke immer mit bedacht und sparsam einsetzen. Ein vollständiges Abklemmen aller Exceptions per try except Block ist fast immer schlecht aus Sicht der Wartung des Programmes. Exceptions sollten nach Möglichkeit nur zur Fehlerbehandlung benutzt werden, AUCH wenn man reine Ausnahmebedinungen damit realisieren könnte. Diese Regel dient zur Vorbeugung von Mißverständnissen durch andere Programmierer. Bestes Beispiel ist die häufigste Frage von INDY Anwendern, was "Connection closed gracefully" denn für ein Fehler ist ?! Exceptions sind also KEIN Ersatz für Funktionen und deren Rückgabe-Werte. Exceptions sind hier vergleichbar mit der Möglichkeit ALLES Objectorientiert und basierend auf Kompoenenten zu codieren, auch wenn eine kleine Funktion ausreichen würde. Auch die OOP und Komponenten sind KEIN Ersatz der prozeduralen Programmierung. Das hinterhältige an Exceptions ist deren Fähigkeit das Black-Box-Prinzip der prozeduralen Programmierung zu zerstören !! Exceptions also NUR auslösen wenn eine Bedingung eintritt in der der Programfluß KEINESFALLS fortsetzbar ist. Gruß Hagen |
Re: Exception Handling.
Super. Danke.
Zitat:
|
Re: Exception Handling.
Achso, das spielt aber in dem Fall keine große Rolle. Ich habe es nur deshalb so gemacht weil ja Stream.Free im finally end; Block ebenfalls eine Exception auslösen könnte. Der äussere try except Block fängt also auch diese Ausnahme dann ab. Da der try except Block alle Excetions abfängt und nicht mit raise weiterleitet, ist es besser so vorzugehen. Es trifft der Spruch zu "Ausnahmen bestätigen die Regel". D.h. in diesem Falle, wo gleich nach dem try finally der except end Block kommt finde ich es besser so herum.
Mann, ist schwierig zu erklären wenn man solche Sachen nur einmalig vor Jahren durchdacht hat und nun nur noch rein gefühlsmäßig es als richtiger empfindet, du weist ja was ich meine :) Manche Sache verstehe ich halt aus dem Bauch heraus einfacher als sie dann einem anderen logisch richtig zu erklären. Gruß Hagen |
Re: Exception Handling.
Zitat:
Ich denke mal in Delphi ist es auch nicht anders als in C++, wenn eine Exception innerhalb eines Objektes geworfen wird, so kümmert sich der Laufzeitcode ums Aufräumen. Gleiches gilt auch bei einfachen Funktionen (dank Stack-Unwind). Wie man sehen kann, gibt es doch so einige Sachen wo Exceptions sinnvoll sind. Übrigens, nur weil man bestimmte Sachen auch mit True/False lösen kann, heißt es nicht, daß dies immer effektiv wäre. Eine Exception erlaubt dir zB den Code für deine Fehlerbehandlung zu zentralisieren. Das würdest du sonst nicht erreichen können. Warum? Weil im Prinzip nach jeder IF-Abfrage auch ein Fehlerbehandlungsaufruf kommen sollte. Zitat:
Zitat:
- Eine Punktklasse, deren 2 Properties X und Y public sind - Eine Stringklasse Jetzt darfst du entscheiden, wann das Geheimnisprinzip angebracht ist und wann nicht. Der Punkt ist nämlich folgender: dieses Prinzip existiert, damit sich der NUTZER einer Klasse keine Gedanken darüber machen muß, wie der PROGRAMMIERER es implementiert hat. Zitat:
Wie mir scheint, hast du an noch keinem wirklich großen Projekt gearbeitet, weil du sonst Exceptions zu schätzen wüßtest. Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Exception Handling.
Hi Oliver,
um meine Meinung besser vertreten zu können müssen wir erstmal unsere Diskussions-Grundlage definieren. Ich betrachte Exceptions und Schutzblöcke erstmal aus rein syntaktischer Sichtweise,also unabhänig von C/C++ oder den Hardware Exceptions in den Intel CPU's. Zitat:
Zitat:
Mein Argumentation basiert auf der Idee der Abstraktions-Ebenen innerhalb eines Programmes. Es gibt Funktionale Bereiche in der Sofwtare die auch heute noch procedural programmiert sind, siehe die RTL eines Delphi programmes. Dann gibt es andere Bereiche wie die VCL, deren unterteilung in reine OOP Überbaue und deren interne Zugriffe auf ordinäre Windows API procedurale Schnittstellen. Das Exception Handling muß sich hier nun auf sinnvolle Weise integrieren. Und damit kommen wir zu dem Punkt das man in RTL Funktionen zb. mit Rückgabewerten statt Exceptions arbeiten sollte. Das ist meine ganz persönlich Erfahrung, wohlgemerkt. Zitat:
Somit unterstützt deine Gegenbehauptung nur meine ursprüngliche Aussage ;) Denn du hast Recht damit das Exception viel mehr können als das was in reine proceduraler Programmierung machbar ist, das bestereite ich garnicht sondern bestätige es durch meine Aussage. Exception's nur in High-Level Funktionen benutzen, die die Programflußebene darstellen die durch die GUI Events aufgerufen werden. Reden wir hier wirklich von OOP? Zitat:
Zitat:
Nein im Sinne von den konzeptionellen Möglichkeiten die Exceptions bieten, da gebe ich dir absolut Recht. Nur ! was ist das Ziel eines guten Source ? Die Wiederverwendbarkeit und Wartbarkeit durch andere Programmierer.Wenn ich nun ein bischen "intelligenter" programmiere andere aber weniger "intelligent" eben einseitig Exceptions begreifen, WER von beiden sollte besser in der Lage sein seinen eigenen Stilso anzupassen das alle Programmierer den Source verstehen ? ich meine das der "intelligentere" Programmierer anpassen sollte. Und exakt so ist meine Aussage zu verstehen: um häufige Missverständnisse im Exception handlingzu vermeiden sollte man diese nach Möglichkeit wirklich nur zur Fehlerbehandlung benutzen, rein als willkürliche Festlegung. Zitat:
Wenn aber die OOP nur eine andere Form des prozeduralen Konzeptes ist, dann ist die OOP nur eine Fortführung der guten Eigenschaften der prozeduralen Mittel der Programmierung. Man darf und kann beides nicht voneinander trennen. Und mal ehrlich, gerade du müsstest wissen wie Objekte/Klassen in Delphi wirklich funktionieren und du weist auch das man mit rein prozeduraler Programmierung in PASCAL auf jedem Record + Proceduren die Funktionaliät der OOP nachbilden kann. Das Einzigste was dann fehlt ist die erweiterte Unterstützung des Programmieres duch den Compiler selber. Gruß Hagen |
Re: Exception Handling.
Moin Hagen,
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Du hast ja durchaus recht damit, daß es sein kann, daß jemand anderes nicht so smart ist, aber rechtfertigt das, den Code konzeptionell schlechter zu gestalten? Denn wenn ich OOP und prozedurale Programmierung schlecht verquicke, wird dies am Ende mehr Probleme machen. Zitat:
Zitat:
Es ist richtig, daß man manchmal nicht umhinkommt Funktionen im alten Stil zu benutzen - ebenso finde ich es hirnrissig sowas wie Integer als Klassen zu betrachten (Java), aber im Gesamtkonzept sind auch dies Puzzlesteine, welche es erlauben weit komplexere Programme zu schreiben. Zitat:
|
Re: Exception Handling.
Ich mache es mal kurz weil wir uns in unserer Meinung nun doch stärker annähern. Ich bin aber pragmatisch und ein Praktiker ergo:
Zitat:
1.) ich rede von professioneller Arbeit wenn ich von programmieren rede, ergo gehört dazu Teamarbeit und Projekte die über längere Zeiträume von verschiedenen Leuten gepflegt werden müssen. In solchen Szenarien ist es unabdingbar das sich die Programmierer auf ein gemeinsammes Niveau verständigen, und das sollte im "allgemeinen Wissenstand" gehalten werden. Außnahmeprogrammierer müssen sich in solchen Teams zurücknehmen. 2.) wir schauen in die Geschichte der Computerei, und stellen fest das sich eben fast NIE das durchgesetzt hat was das Beste darstellte. Es setzte sich immer das durch was im allgmeinen Interesse rundum praktisch war. Also das was sich auch gut vermarkten lässt, was Profit bringen wird und denoch technisch einigermaßen gelungen war. Also nicht das was hypothetisch supergut war aber zb. keinerlei Lobby dahinter hatte. Fazit: als Pragmatiker konzentriert man sich auf das Realistische und nimmt wohlweislich Kompromisse in kauf. Und nun aus Erfahrungswerten heraus in Delphi die Exceptions als reine fehlermeldungen zu betrachten halte ich in diesem Sinne als annehmbare Schwäche. D.h. natürlich nicht das die Programmierer mit Wissensdefiziten mehr über Exceptions im Gesammtkontext lernen müssen. Meine diesbezügliche Aussage war wie oben schon angedeutet nur ein Tipp von mir an andere Programmierer. Daran halten muß sich keiner, aber aus meiner Erfahrung heraus verhindert man so viele Mißverständnisse. Ich errinnere nur an die "EConnectionClosedGracefully" Exception in den INDY's. Zum zeitpunkt der Veröffentlichung waren sehr viele heise Diskussionen in den NewsGroups die sich exakt um die Frage drehten -> ist eine Exception nur ein Fehler oder eine Ausnahme die nicht zwangsläufig ein Fehler sein muß. Man kann sich streiten, jahrhunderte lang, oder man wird in einer Firma einfach willkürlich eine Festlegung treffen. Und in unserer Firma heist diese "Exception = Fehler". (Ausnahmen bestätigen dann nur die Regel). Gruß Hagen |
Re: Exception Handling.
Exception ansich sind ein sehr wichtiges Mittel der Programmierung geworden. Sie stellen sozusagen einen parallel zum eigentliche Programablauf feststehenden Programmablaufplan dar. Und exakt in dieser Parallelität sehe ich auch gleichzeitig die Schwäche der Exceptions. Denn im Denkvermögen der meisten Programmierer stellt sich ein Program am einfachsten als simple und rein sequientielle Steuerung dar. Exception widersprechen also unserem Denkvermögen, meistens zumindestens. Nun, das bedeutet das asynchron eintreffende Exceptions oder auch Events immer eine Denk-Hürde für die meisten Programmierer darstellen, ergo potentielle Fehlerquellen. Das schlimme daran ist es das diese Fehlerursachen meistens noch viel stärker Nachfolgefehler nach sich ziehen.
Meine Lösung aus diesem Dilema lauten: 1.) wenn Exception dann aber auch immer try finally benutzen um resourcen zu schützen, das verringert die Anzahl unerwünschter Nachfolgefehler falls eine Exception/Event asynchron den Programfluß unterbricht. 2.) Wissen, die Programmierer habe zu lernen wie asynchrone Programme per Exceptions/Events zu arbeiten haben 3.) Beschränkungen, durch willkürlich Beschränkungen auf gemeinsam benutzte und Firmeninterene Vorgaben im Arbeitsstil. 4.) das Eintreffen/Auslösen solche asynchrone Exceptions/Events möglichst reduzieren. Besonders eben in RTL/Helper Funktionen sollte man auf Exceptions verzichten und stattdessen über Rückgabewerte einen Fehler signalisieren. Nichts ist schlimmer wenn eine solche Funktion tief im Inneren eines Programmes ständig Exceptions auslösst. Das führt dann dazu das der Programmierer wie wild alle möglichen Exception abfängt und somit den Nutzeffekt solcher Exceptions auf Null reduziert. Denn eine Exception macht meistens nur dann Sinn wenn sie auch sichtbar wird und somit die Ausnahme auch ersichtlich ist. Gruß hagen |
Re: Exception Handling.
Ah eines habe ich noch vergessen. Betrachten wir einfach mal eine Funktion X
Delphi-Quellcode:
Wir SEHEN hier eine Schnittstelle zu einer Black-Box. Wie SEHEN einen Namen X(), einen Eingabeparameter Input und einen Ausgabe Paramater Output. Das alles ist super und entspricht absolut dem Black Box Prinzip.function X(Input): Output; Nun führen wir aber in diese Funktion in deren Implementation eine Exception ein. Wird diese ausgelösst so zerstört sie das Black-Box-Prinzip denn WIR SEHEN diese Exception NICHT in der Schnittstelle !!?? Exception sind also einerseits ein produktives Mittel aber andererseits ein möglicherweise kontrapoduktives Design da sie aus Sicht der Schnittstelle nicht sichtbar sind. Nun, die RTL, VCL etc.pp. enthalten 1000'ende von Funktionen, Methoden und Events und bei keinem sehen wir ob der Source darin auch Exception auslösen kann. Wie reagieren die Programmierer auf diesen Umstand ? Entweder sie analysieren jede Sourcezeile all dieser Funktionen (was im Grunde schwachsinnig gegen das Black-Box-Konzept verstösst oder aber der Programmiere beginnt wild umsich zu try except'en und zerstört somit im Grunde den Nutzen der Exceptions. Das sind die Folgen eines schlechten Designes, und die Ursache liegt darin begründet das wir in der Schnittstelle unserer Funktionen/Merthoden etc. pp. eben nicht SEHEN welche Exception zu erwarten sind. Gruß Hagen |
Re: Exception Handling.
Zitat:
Beispiel: In der Firma, auf die ich anspiele, werden 2 Systeme eingesetzt um die Oberfläche für Kontrollstationen in Fabriken zu designen. Nun ist es so, daß ich mich krampfhaft an dem Design der einen (in der Firma beliebteren) Software orientieren mußte und deshalb in zahlreiche Schwierigkeiten kam. Stattdessen wäre es möglich gewesen eine Bibliothek aus für beide Systeme einheitlichen ActiveX-Controls zu erstellen, die dann für ein einheitliches Aussehen und "corporate identity" gesorgt hätten. Das ist ist aber unbeliebt, weil man es nicht direkt an den Kunden weitergeben kann (bzw. dann bei einer Ausschreibung schlechter dasteht). Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Der letzte fängt die Exception :zwinker: Zitat:
Um es auf einen Punkt zu bringen Für mich gehören try-except-Blöcke in die OOP-Welt und try-finally-Blöcke in die PP-Welt. Daß sie bei Delphi vermischt auftreten, haben wir der Tatsache zu verdanken, daß sie Objekte in Delphi nicht wie Stackvariablen verhalten (man sie also explizit freigeben muß, was oft in try-finally-Blöcken geschieht). Nichts dagegen, daß dies überhaupt möglich ist (auch in C++ kann ich über Objektzeiger gehen), aber in Delphi gibt es eben nur diese eine Möglichkeit. Ich gehe aber davon aus, daß Delphi.NET dafür eine Antwort hat. Robert_G? Anwesend? Sag mal was :mrgreen: Korrektur: Natürlich geht es auch in Delphi (nämlich mit Interfaces), aber eben nur über Umwege. Und wenn jetzt irgendjemand kommt und das unsägliche object vorschlägt, dann bin ich aber raus aus der Diskussion. Das existiert bekanntlich nur für die Kompatibilität mit älteren Quelltexten. EDIT2: jbg's Einwand eingearbeitet. |
Re: Exception Handling.
Zitat:
Natürlich bietet Delphi.NET da eine Möglichkeit: Das .Free einfach weglassen (sofern die Klasse kein Dispose() braucht). Um auf das angeben von Exceptions im Interface zu kommen. Java hat dies in die Syntax eingebaut: "void bla() throws MyException". Das macht meines erachtens aber nur solange Sinn, wie man die Exceptions direkt vor Ort behandelt. Denn wenn man die nun durchzuschleifen probiert, hat man das Problem, dass man bei allen Methoden das throws anpassen muss, was bei größeren Projekten (mit einer kleinen Änderung im Kern: neue Exception) dann zu einer Sisyphusarbeit ausartet. |
Re: Exception Handling.
Hi Olli,
es ist schwer eine solche Grundsatzdiskussion über das Netz zu führen. Lieber würde ich sowas in einer persönlichen Diskussion klären. Denn im Grunde vertreten wir ähnliche Ansichten, der Unterschied zwischen uns besteht nur darin aus welchem Winkel wir die Problematik betrachten und was für Schlüsse wir letzendlich daraus ziehen. Zitat:
In beiden Fällen bedeutet ein solcher "Vorgang" eben Zeit, somit Geld und Nerven. Das verkompliziert sich noch wenn man nun Funktionen benutzt die wiederum Funktionen benutzen die im Grunde alle eine Exception auslösen können. Ergo: Der Programmierer müsste auf Grund der fehlenden Information wo und wann Exceptions ausgelösst werden entweder die "auf Nummer sicher gehen Methode" anwenden und alle Exceptions permanent abfangen, oder aber die "Ignorieren Methode" und verzichtet auf die aktive Einflußnahme des Programmablaufes beim Eintreffen einer Exception. BEIDES ist aber kontraproduktiv, denn ein programmiertes Feature macht nur dann effektiv Sinn wenn es mir Arbeit abnimmt und einsparrt, und nicht zus. Zeit kostet weil es im Grunde in der Schnittstelle undokumentiert ist. Und schwups sind wir bei der Dokumentation. Ein sauberer Source sollte komplett ohne Doku auskommen können, denn warum sollte Goethe ein Buch schreiben um eine Dokumentation zum Lesen eines seiner anderen Bücher zu haben. Das ist doch idtiotisch, und im übertragenen Sinne ist es noch idiotischer auf einen Source bezogen. Denn in dieser Form der Sprache haben WIR die Regeln festgelegt und diese Regeln arbeiten immer nach dem Prinzip einer öffentlichen Schnittstellendeklaration und einer Black Box Implementation. Leider verstoßen aber immer mehr OOP Ansätze exakt gegen dieses Konzept. Exceptions sind nicht in der Methoden/Funktions Deklaration kenntlich gemacht, sie werden innerhalb der Black Box ausgelösst. Aus diesem Grunde geht der Vorteil der Schnittstelle und dem Black Box Prinzip kaputt denn der Entwickler muß nun doch wissen ob innerhalb der Black Box eine Exception auftreten kann. Ergo: der Source muß dies separat dokumentieren, er wird also auf Grund seiner Schnittstellen Deklaration nicht mehr selbsterklärbar sein, und schwups wieder ein alter Vorteil verloren. Ich möchte hier nicht gegen Exceptions argumentieren, sondern eher das dahinterliegende Konzept im Gesamtkonzept der Programmiermethoden analysieren. Und deshalb meinte ich eingangs das Exception tatsächlich in jedlicher Weise eine Ausnahme darstellen. Der Grad ihrer Nützlichkeit ist also sehr schmal für uns Programmierer. Gruß Hagen |
Re: Exception Handling.
Jetzt gebe ich auch noch mal meinen Senf dazu (obwohl ich den immer noch nicht mag...)
1. Bedeutung des Begriffs Exception Für mich widerspricht es dem eigentlichen Sinn wenn man Exceptions dazu benutzt, einfache Fehlerbedingungen zu signalisieren. Für mich ist eine Exception im wörtlichen Sinne eine Ausnahmebedingung: eine Programmzustand, in dem nicht mehr mit der normalen Ausführung fortgefahren werden kann. Nicht genug Speicher, unzureichende Systemresourcen, die CD mit der EXE-Datei des Programms wurde aus dem Laufwerk genommen oder ähnliches, und sicher auch Programmierfehler wie fehlerhafte Indizes. Ganz bestimmt aber nicht so etwas wie ein einfacher Fehlerzustand. Der kann zwar stellenweise asynchron auftreten, also ohne direkten Bezug zur aktuell aufgerufenen Prozedur, aber ich denke das ist kein Grund aus einem Ereignis (-> Event) eine Exception (-> Ausnahmebedingung) zu machen. 2. Was der "normale" Programmierer daraus macht In meinen Augen macht ein Block wie
Delphi-Quellcode:
überhaupt keinen Sinn - da kann ich die Exception auch gleich bis zum obersten Level durchschlagen lassen, dort wird nämlich auch einfach eine Meldung ausgegeben. Diese Form sieht man aber am häufigsten. Noch schlimmer ist, dass die meisten Programmierer dann den Fehler machen, einfach im Code fortzufahren als ob nichts geschehen wäre, der try-except Block in diesem Fall also gerade eine Dummheit ist.
try
TueIrgendWas; except on E: Exception do MessageDlg(E.Message, mtError, [mbOk], 0); end; 3. Was ich daraus mache Persönlich benutze ich Exception-Handling fast ausschließlich für try-finally Blöcke zum Ressourcenschutz. Nur an bestimmten Stellen verwende ich try-except und zwar dort, wo ich mit einem möglichen Fehlschlagen rechne. Z.B. wenn ich ein TFileStream ohne vorherige Abfrage mit FileExists öffne, was ja sowieso wenig Sinn macht, denn es kann ja sein dass es die Datei gibt aber ich sie trotzdem nicht öffnen darf. 4. Der Nutzen Praktisch finde ich durchaus, dass SEH den Sourcecode übersichtlicher macht. Ich finde z.B. die erste Variante wesentlich besser zu lesen als die zweite:
Delphi-Quellcode:
Ich kann mich noch gut an die ersten Code-Beispiele von Microsoft zur Windows-API / GDI-Programmierung erinnern, wo hinter jedem Prozeduraufruf (GetDC, CreateBrush, SelectObject, DeleteObject, ReleaseDC) immer wieder der ganze Salmon hing, also jedesmal eine separate Fehlerbehandlung mit abschließendem return. Ich denke da ist SEH die bessere Alternative.
procedure Variante1;
begin TueDies; TueDas; if StimmtDas then TueIrgendWas else TueWasAnderes; // Kein Rückgabewert notwendig, Exception // schlägt durch in die aufrufende Routine end; function Variante2: boolean; begin if TueDies <> 0 then Result = false //<-- 1 signalisiert Fehler else if TueDas in [0, 7, 24] then Result = false //<-- 0 = nicht gefunden, 7 = zu groß, 24 = zu alt else case StimmtDas of 5, 13: Result = false; //<-- Fehlercodes 1: Result := TueIrgendWas = 1; 0: Result := TueWasAnderes = 0; end; end; <Vorbehalt="Muss ich noch mal genau drüber nachdenken"> 5. Exceptions zur Fehlerbehandlung Um eine Exception für die normale Fehlerbehandlung zu "missbrauchen" fehlt ein ganz wichtiges Feature, nämlich das Fortfahren an der Fehlerstelle. Im Grunde so etwas wie das "Resume Next" in Visual Basic. Dann könnte man nämlich in Fällen wie beim "EConnectionClosedGracefully" den Fehler dankend zu Kenntnis nehmen und an der alten Stelle fortfahren. </Vorbehalt> Hinweis: Nachträglich noch etwas editiert, um es strukturierter aussehen zu lassen :angel2: |
Re: Exception Handling.
Zitat:
Zitat:
Zitat:
Zitat:
Nochmal mein Beispiel. Ich habe eine Molekülklasse geschrieben, die intern sog. Tripods ("Dreibeine") benutzt um 2 Atome und 3 Bindungen darzustellen. Nach außen ist das nicht sichtbar. Im Sinne der Effizienz ist es nun so, daß man von außen auf beliebige Bindungen oder Atome zugreifen kann (auch nichtexistente!). Intern ist es wie gesagt ein 2D-Array dieser Tripods. Ist der Zugriff nicht erfolgreich ("out of range") behandele ich die Exception lokal und erweitere das Array und ggf. erstelle ich das vorher nicht vorhandene Tripod-Objekt. Hier werden Exceptions sehr sinnvoll eingesetzt und sind nach außen nicht sichtbar. Zitat:
Zitat:
Zitat:
Zitat:
Allein die Wahl eines Methodennamens muß ja nicht sonderlich clever sein. Wenn die Dokumentation existiert ist's aber kein Problem. Zitat:
Zitat:
|
Re: Exception Handling.
Keine rote Box. Sorry, will dich natürlich nicht ignorieren ;)
Ich stimme in den Punkten 1-4 vollständig mit dir überein. Zitat:
Zitat:
Aber wenn wir schon bei Windows' SEH sind. Das bietet das. Nur die meisten Compiler machen nicht komplett Gebrauch von allen Features (bzw. reichen nicht alle zum Benutzer/Programmierer durch). Genaugenommen kannst du an einer beliebigen Stelle deines Codes mit beliebiger Registerbelegung fortfahren. Wenn die Exception aber vorher durchschlug, wurde der Stack schon aufgeräumt, will heißen ein Fortfahren ist nicht ohne weiteres möglich. Der Stack müßte gesichert und wiederhergerstellt werden um das zu ermöglichen. |
Re: Exception Handling.
Hi Olli
Zitat:
Zitat:
Nein. Es sollte garkeine Dokumentation geben, das wäre am besten !! Die Programmier-Sprache sollte auf jegliches Element das eine Information zu einer Information liefert verzichten. Denn es kann niemals eine Information zu einer Information geben, das ist unlogisch. Die eigentliche Information verkommt zu einer redundanten und schon bekannten Nachricht. Eine zusätzliche Dokumentation eines Sources wäre vergleichbar mit einer Information zu einer Nachricht. Eine Nachricht enthält aber keine für uns neue und nützliche Information. Somit wird der eigentlich geschrieben Source zu einer belanglosen Nachricht und IN der Dokumentation steckt eigentlich exakt das drinnen an Information die in den Source gehören würde. Der Programmierer hat geschlampt. Die IDEALE Programmier-Sprache ist eine Sprache die ein Problem für die Machine und den Menschen absolut ohne zus. Informationen in Form von Dokumentationen erklärbar und verständlich macht. Schau mal, auch wir brauchen in unserer natürlichen Sprache keine zusätzliche Sprache um das was wir ausdrücken wollen nochmals in dieser zus. Sprache, genannt Dokumentation, deutlich auszudrücken. Wir benötigen nur eine Sprache und mehr nicht. Wir sollten und werden eines Tages in einer Programmiersprache programmieren die exakt diesen Forderungen entspricht und sehr sehr ähnlich zu unserer natürlichen Sprache funktioniert. Aber am wesentlichsten an dieser Sprache wird es sein das es keine Nachrichten gibt, sprich sie wird in jeder Quelltextzeile absolute Information enthalten, ohne Redundanz und ohne Nachrichten. Man kann sich nun streiten ob eine Dokumentation zu einem Source + dem Source ein komplettes Program darstellt, sprich zwei untrennbare Bestandteile des Programmes sind. Ich behaupte mal nicht, denn nur der eigentliche Source stellt den Ablaufplan des Programmes dar. Die ideale Sprache würde also reinen Source enthalten und denoch wäre dieser Source gleichmaßen ideal verständlich für uns Menschen und gleichermaßen ideal übertragbar auf eine Machine. Gruß Hagen |
Re: Exception Handling.
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Exception Handling.
Ich finde Exceptions sind eine gute waffe gegen Funktionen mit booleschem Rückgabewert ;)
Wenn in der JVCL bei den Indies aber "Verbindung erfolgreich geschlossen" eine Ausnahme darstellt, würde ich mir überlegen, warum ich diese komponenten benutze( :mrgreen: ) - am Ende sind wir bei Java, wo ja gelegentlich "Fatal Error: No Error" gemeldet werden soll... Eigentlich wollte ich ja auf die redundanz zu sprechen kommen: Redundanz ist gut, wenn es um komplexe zusammenhänge geht, bei denen hintergrundinfo hilft. redundanz ist schlecht, wenn es um bandbreite geht :mrgreen: Natürlich wäre ein Programmierstil, der keine Dokumentation mehr verlangt, ideal. Aber würde das nicht in deskriptiven Sprachen(ergo Overhead=Redundanz), oder zumindest in Funktuionsdeklarationen wie
Delphi-Quellcode:
enden? ohne redundanz geht es nicht - ob jetzt mit oder ohne Kommentaren, Dokumentation oder anschaulichen Funktionsnamen.
function A_und_B_werden_und_addiert_ausgegeben(A,b:integer):integer
Denn ist nicht schon add(a,b:integer):integer ein enormer overhead? viel effektiver wäre doch f 372(x,y:2):2, oder? aber wie bereits gesagt: je komplexer das thema/die methode, desto mehr redundanz braucht der mensch. Auch wenn der Code noch so schön eingerückt ist, solange man nicht Neo heisst, wird man kaum aus dem reinen Lesen des Quellcodes das Programm vor sich Ablaufen sehen. Zumindest bei genügender Komplexität des Problems. ich fasse zusammen: Soviel Redundanz wie nötig, so wenig Dokumentation wie möglich. Aber bitte nicht auf koreanisch ;) EDIT: sachlichen fehler korrigiert. Sorry an die JVCL ;) |
Re: Exception Handling.
Zitat:
|
Re: Exception Handling.
Aus der Sicht des gesunden Menschenverstandes. :zwinker: (Ich behaupte ja nicht, dass ich einen hätte :mrgreen: )
Zu viel Redundanz bedeutet: Man braucht länger, bis man es verstanden hat, da man ja das unnütze Beiwerk mitlesen muss. Zu wenig aber bedeutet, dass man evtl. nicht genügend information bekommt, nämlich die, die der autor vorausgesetzt und deswegen weggelassen hat. Für den Benutzer sollte natürlich z.B. eine GUI so selbsterklärend sein, dass er keine Dokumentation braucht. Aber auch hier gilt wieder: Mit zusätzlicher Komplexität - z.B. Funktionen, die nur selten benutzt werden - braucht man eine Dokumentation. Code ist aber selten selbsterklärend - deshalb sollte sich der Programmierer schon die Mühe machen, hier und da oder zumindest bei mathematischen Operationen Kommentare einfließen zu lassen. Auch eine Klasse KANN selbsterklärend sein - sobald das ganze allerdings über ein paar Funktionen wie savetofile, draw und create hinausgeht, muss man eine Dokumentation zur Verfügung stellen. |
Re: Exception Handling.
Zitat:
Zitat:
|
Re: Exception Handling.
Zitat:
Hier mal eine Information: Apfel, Pferd Was will ich dir damit wohl sagen? All die schmückenden Worte sind redundant, aber nur mit denen kannst du überhaupt verstehen, was ich dir sagen will. Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Exception Handling.
stimme euch zu, und habe den fehler korrigiert.
Zum unnützen Beiwerk: Man muss sich einfach überlegen, welchen Wissensstand der durchschnittliche Leser der Dokumentation hat - desto geringer dieser, desto mehr Kontext braucht man. In einem Mathebuch für Gymnasiasten steht mehr drin als in einer Formelsammlung für Ingenieure. Und genau das meine ich damit. |
Re: Exception Handling.
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz