![]() |
Delphi-Version: XE2
Dokumentation und Exceptions - Wann muss ich was erwarten?
Das ist schon das (mindestens) dritte Thema zum Bereich Exceptions, ich werde irgendwie immer noch nicht warm damit, wie Exceptions in der Delphi-Welt generell gehandhabt werden.
Dass auf meine generelle Frage ![]() ![]() Ich schaue mir fröhlich von Embarcadero Tutorial: ![]() Dort finde ich:
Delphi-Quellcode:
Abgesehen davon, dass ich nicht einsehe, warum man die beiden Oberflächen-bezogenen Anweisungen nicht nach dem try-Block ausführen sollte: Woher kann ich wissen, dass ich einen
try
// Establish the connection. SQLConnection1.Connected := true; executeButton.Enabled := true; outputMemo.Text := 'Connection established!'; except on E: EDatabaseError do ShowMessage('Exception raised with message' + E.Message); end;
Delphi-Quellcode:
zu erwarten habe?
EDatabaseError
Die Dokumentation erzählt weder bei ![]() ![]() ![]() Und auf Dauer habe ich ehrlich gesagt auch nicht die Zeit, in Methoden wie
Delphi-Quellcode:
zu suchen, was denn hier alles Exceptions nach oben werfen könnte:
TSQLConnection.DoConnect
Delphi-Quellcode:
procedure TSQLConnection.DoConnect;
var ConnectionProps: TDBXProperties; Ind: Integer; ConnectionFactory: TDBXConnectionFactory; LoginParams: TStrings; SchemaOverride: string; SchemaOverrideList: TStringList; Password: string; MemoryConnectionFactory: TDBXMemoryConnectionFactory; begin ConnectionProps := nil; LoginParams := TStringList.Create; MemoryConnectionFactory := nil; try if LoadParamsOnConnect then begin ConnectionFactory := TDBXConnectionFactory.GetConnectionFactory; TDBXConnectionFactory.Lock; try ConnectionProps := ConnectionFactory.GetConnectionProperties(ConnectionName); ConnectionProps := ConnectionProps.Clone; finally TDBXConnectionFactory.Unlock; end; end else begin ConnectionProps := TDBXProperties.Create; try ConnectionFactory := TDBXConnectionFactory.GetConnectionFactory; except MemoryConnectionFactory := TDBXMemoryConnectionFactory.Create; ConnectionFactory := MemoryConnectionFactory; ConnectionFactory.Open; end; ConnectionProps.AddProperties(FParams); if ConnectionProps.Properties.IndexOfName(TDBXPropertyNames.DriverName) = -1 then ConnectionProps.Add(TDBXPropertyNames.DriverName, DriverName); end; CheckLoginParams; ConnectionState := csStateConnecting; GetLoginParams(LoginParams); if LoginParams.Values[TDBXPropertyNames.Database] <> ConnectionProps[TDBXPropertyNames.Database] then begin ConnectionProps[TDBXPropertyNames.Database] := LoginParams.Values[TDBXPropertyNames.Database]; end; SetCursor(HourGlassCursor); ConnectionProps.Add('UNLICENSED_DRIVERS', IntToStr(GDAL)); // Do not translate. FLoginUsername := LoginParams.Values[TDBXPropertyNames.UserName]; if FLoginUserName <> '' then ConnectionProps[TDBXPropertyNames.UserName] := FLoginUsername; Password := LoginParams.Values[TDBXPropertyNames.Password]; if Password <> '' then ConnectionProps[TDBXPropertyNames.Password] := Password; ConnectionProps.SetComponentOwner(self); ConnectionProps.Events.Events[sValidatePeerCertificate] := TEventPointer(ValidatePeerCertificate); ConnectionFactory.Lock; try FDBXConnection := ConnectionFactory.GetConnection(ConnectionProps); finally ConnectionFactory.Unlock; end; for Ind := 0 to FMonitorUsers.Count -1 do FMonitorUsers[Ind].UpdateTraceCallBack; SetCursor(HourGlassCursor); RegisterTraceCallback(True); FDefaultSchema := ''; if (FDBXConnection.ProductName = 'BlackfishSQL') then {Do not localize} begin FDefaultSchema := 'DEFAULT_SCHEMA'; { Do not localize } end; SchemaOverride := ConnectionProps[TDBXPropertyNames.SchemaOverride]; if (SchemaOverride = '') and LoadParamsOnConnect then begin TDBXConnectionFactory.Lock; try SchemaOverride := ConnectionFactory.GetDriverProperties(ConnectionProps[TDBXPropertyNames.DriverName]) [TDBXPropertyNames.SchemaOverride]; finally TDBXConnectionFactory.Unlock; end; end; if SchemaOverride <> '' then begin SchemaOverrideList := TStringList.Create; try SchemaOverrideList.Delimiter := '.'; SchemaOverrideList.DelimitedText := SchemaOverride; if SchemaOverrideList.Count = 2 then begin if (SchemaOverrideList[0] = '%') or (SchemaOverrideList[0] = FLoginUsername) then FDefaultSchema := SchemaOverrideList[1]; end; finally SchemaOverrideList.Free; end; end; ConnectionOptions; ConnectionState := csStateOpen; finally FreeAndNil(MemoryConnectionFactory); // If allocated, free it. SetCursor(DefaultCursor); LoginParams.Free; ConnectionProps.Free; if ConnectionState = csStateConnecting then // an exception occurred begin ConnectionState := csStateClosed; if Assigned(FDBXConnection) then FreeAndNil(FDBXConnection) end; end; end; Deshalb: Irgendwoher muss die Doku ja die Weisheit nehmen, dass irgendwoher ein
Delphi-Quellcode:
kommen kann. Ich bin zu dumm, das zu sehen. Bitte helft mir.
EDatabaseError
|
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
ist doch ganz einfach: Du weißt das aus der Dokumentation - in diesem Fall aus dem entsprechenden Codebeispiel. Der Doku-Schreiber weiß, dass da ein EDatabaseError aufschlagen kann (so lange er keinen Fehler gemacht hat) vom Entwickler, der die betreffende Bibliothek geschrieben hat, und der sollte das eigentlich wissen. Zitat:
Grüße |
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Ein "So basteln Sie sich eine XYZ-Anwendung"-Tutorial das ich zufällig über Google gefunden habe ist doch keine Dokumentation :evil:, auch wenn es von Embarcadero stammt. Finde ich.
Würde es in der Doku angesprochen werden, würde ich das noch vielleich gelten lassen. Aber das steht einfach nur so im Raum. Woher weiß ich, was noch für Arten von Fehlern nach oben geworfen werden können? Ich möchte nicht dort enden, aus Unwissenheit grundsätzlich Exceptions zu essen, nur weil Embarcadero nichts vernünftig dokumentiert hat (falls das denn der Fall sein sollte)... |
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Wichtig scheint mir das nur wenn Du auf verschiedene Exceptions unterschiedlich reagieren willst, was dann ja voraussetzt dass Du weist was Du wie behandeln will. Den Rest kannst Du ja über den else Zweig abfackeln.
Delphi-Quellcode:
procedure Tuwas(const s:String);
var i:Integer; begin try i := StrToInt(s); Showmessage(FloatToStr(1/i)); except ON E:Exception do begin if E is EZeroDivide then Showmessage('Divison durch 0') else Raise; // oder was auch sonst hier vorzusehen wäre end; end; end; procedure TForm3.Button1Click(Sender: TObject); begin TuWas('0'); TuWas('Nase'); end; |
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
Zitat:
|
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
Ich möchte ja nur überhaupt wissen, dass da etwas kommen kann! Meine Beschwerde ist bislang nur: "Zufällig sah ich in diesem zusammenhangslosen Tutorial, dass diese Methode eine Exception werfen kann. Das steht weder in der Doku, noch ist es in der Implementation ersichtlich. Das darf nicht sein, Embarcadero!"Weiterhin die Hoffnung, dass die Beschwerde unbegründet ist. Zitat:
|
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
Wobei es immer Ausnahmen von der Regel gibt und auch selten, ganz selten, ein Exceptionhandler sinnvoll sein kann, welcher wirklich alles schluckt. An dem Thema entzündeten sich fraglos schon Religionskriege. |
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
|
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Zitat:
Zitat:
1. du sicher stellen musst, dass gewisse Anweisungen immer bearbeitet werden (try-finally) 2. bei erwarteten Fehlern (Datei nicht gefunden, Passwort nicht eingegeben) ein alternativer Programmzweig aufgerufen wird, der dem Anwender die Möglichkeit bietet zu reagieren oder um den Fehler zu loggen und für eine spätere Korrektur vorzumerken ... |
AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
Also ein Code wie dieser macht überhaupt keinen Sinn:
Delphi-Quellcode:
1. Problem:
try
// Establish the connection. SQLConnection1.Connected := true; executeButton.Enabled := true; outputMemo.Text := 'Connection established!'; except on E: EDatabaseError do ShowMessage('Exception raised with message' + E.Message); end; der Programmfaden läuft weiter obwohl das Öffnen der Datenbank erfolglos was. In der Folge wird es im weiteren Verlauf zu einer weiteren Exception kommen. 2. Problem: das Abfangen der Exception und das
Delphi-Quellcode:
vernichtet wichtige Informationen zur Fehlersuche.
ShowMessage
Es geht z.B. die genaue Exception-Klasse verloren. Die Fehlermeldung "Exception raised with message" ist absolut Aussagelos, es steckt keine nützliche Information drin. 3. Problem: durch das Abfangen der Exception, geht die Möglichkeit verloren die Exception automatisch in eine Logdatei zu schreiben. Wie macht man es dann richtig? 1. Regel: Exceptions nur dann abfangen, wenn man irgendwas zur Verbesserung beitragen kann. Die Idee man könnte eine Exception durch abfangen irgendwie "reparieren" ist sowieso in 99% aller Fälle falsch. Was man allerdings tun kann ist dem Benutzer möglichst gute Informationen zu geben was die Ursache b etrifft und wie er das Problem beheben kann.
Delphi-Quellcode:
2. Regel:
try
// Establish the connection. SQLConnection1.Connected := true; executeButton.Enabled := true; outputMemo.Text := 'Connection established!'; except on E: Exception do begin // dem Benutzer und dem Programmierer eine aussagekräftige Meldung geben // allgemeine Beschreibung zu Beginn der Meldung E.Message := 'Fehler beim Öffnen der Datenbank'#13#10+ E.Message + #13#10 + // Details für Programmierer am Ende der Message SQLConnection1.ConnectionString; // um den Benutzer optimal zu unterstützen kann man den HelpContext setzen E.HelpContext := HELPCONTEXT_CONNECTION_FAILED; raise; // Exception neu auslösen, Exceptionklasse bleibt erhalten end; end; nicht Exceptions abfangen und irgendwas mit
Delphi-Quellcode:
anzeigen.
ShowMessage
Showessage hat in dem Code nicht verloren. Stellt dir vor die Exception tritt in einer Schleife mit >100 Durchläufen auf. Ständig popt die gleiche Meldung auf; man kann das Programm nur noch abschiesen 3. Regel: Man sollte
Delphi-Quellcode:
wesentlich öfters verwenden als
raise
Delphi-Quellcode:
.
try...except
In dem man eigene Exception-Klassen deklariert und diese dann raised kann man besondern in großen Programmen die Fehlerursache genauer lokalisiert. 4. Regel: Das Prinzip der Informationanreicherung schachteln
Delphi-Quellcode:
Selbst bei einer Zugriffsverletzung innerhalb von CalcTaxInternal() bekommt der Benutzer und der Programmierer so eine gute Info wo das Problem ist.
procedure TDataModule.CalcTaxInternal(IdCompany:Integer);
begin // jede Menge komplizierter Code zur Steuerberechnung ... end; procedure TDataModule.CalcTax(IdCompany:Integer); begin try CalcTaxInternal(IdCompany); except on E: Exception do begin E.Message := 'Fehler bei Steuerberechnung für Firma '+IntToStr(IdCompany)#13#10+ E.Message: E.HelpContext := HELPCONTEXT_TAX_CALC_ERROR; raise; // Exception neu auslösen, Exceptionklasse bleibt erhalten end; end; Die eigentliche Steuerberechnung bleibt völlig frei von störenden
Delphi-Quellcode:
.
try ... except
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12: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