Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Dokumentation und Exceptions - Wann muss ich was erwarten? (https://www.delphipraxis.net/175209-dokumentation-und-exceptions-wann-muss-ich-erwarten.html)

Der schöne Günther 6. Jun 2013 09:52

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 http://www.delphipraxis.net/173885-w...exception.html keine eindeutige Antwort existiert ist natürlich verständlich. Mein jetziges Anliegen ist stark verwandt mit meiner unbeantworteten Frage http://www.delphipraxis.net/174454-u...ns-finden.html :

Ich schaue mir fröhlich von Embarcadero Tutorial: Herstellen einer Verbindung zu einer SQLite-Datenbank (Delphi) an.

Dort finde ich:
Delphi-Quellcode:
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;
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
Delphi-Quellcode:
EDatabaseError
zu erwarten habe?

Die Dokumentation erzählt weder bei TSQLConnection::Connected noch TCustomConnection::Connected von einer möglichen Exception. Immerhin spricht sie von einer intern aufgerufenen Methode TCustomConnection.DoConnect. Doch auch hier: Kein Sterbenswort.

Und auf Dauer habe ich ehrlich gesagt auch nicht die Zeit, in Methoden wie
Delphi-Quellcode:
TSQLConnection.DoConnect
zu suchen, was denn hier alles Exceptions nach oben werfen könnte:

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:
EDatabaseError
kommen kann. Ich bin zu dumm, das zu sehen. Bitte helft mir.

Lemmy 6. Jun 2013 11:14

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1217629)
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
Delphi-Quellcode:
EDatabaseError
zu erwarten habe?
....
Deshalb: Irgendwoher muss die Doku ja die Weisheit nehmen, dass irgendwoher ein
Delphi-Quellcode:
EDatabaseError
kommen kann. Ich bin zu dumm, das zu sehen. Bitte helft mir.


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:

Zitat von Der schöne Günther (Beitrag 1217629)
was denn hier alles Exceptions nach oben werfen könnte:

das ist eigentlich Aufgabe der Dokumentation, das zu beschreiben...

Grüße

Der schöne Günther 6. Jun 2013 11:20

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)...

Bummi 6. Jun 2013 11:37

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;

Lemmy 6. Jun 2013 11:50

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1217636)
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.

Kannst Du blöd finden oder nicht, ändert aber nichts daran, dass es die offizielle Doku ist.


Zitat:

Zitat von Der schöne Günther (Beitrag 1217636)
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)...

wo isst Du Exceptions? Du reagierst auf EDatabaseError und der Rest wird an die gui geworfen (ist zumindest bei Delphi 7 so und wird auch hoffentlich in XEx nicht anders sein)

Der schöne Günther 6. Jun 2013 11:53

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von Bummi (Beitrag 1217637)
Wichtig scheint mir das nur wenn Du auf verschiedene Exceptions unterschiedlich reagieren willst

Vollkommen richtig.

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:

Zitat von Lemmy (Beitrag 1217638)
Kannst Du blöd finden oder nicht, ändert aber nichts daran, dass es die offizielle Doku ist.

Meinetwegen. Aber selbst das ist nicht vollständig. Das Teil kann genauso gut eine TDBXError-Exception werfen. Und die ist mit dem EDataBaseError nicht verwandt. Die fängt der Kerl in seinem Tutorial auch nicht. Und in der Doku vermerkt ist es natürlich auch nicht.

sahimba 6. Jun 2013 11:58

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1217639)
Witzigerweise kann das Ding auch noch ganz andere Exceptions werfen. Und die fängt der Tutorialersteller nicht.

Man "sollte" imho auch nur jene Exceptions behandeln, auf welche man auch wirklich (sinnvoll) reagieren kann. Im Zweifel lässt man sie eben ganz nach oben bubbeln.

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.

Der schöne Günther 6. Jun 2013 12:02

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von sahimba (Beitrag 1217640)
Im Zweifel lässt man sie eben ganz nach oben bubbeln.

Ja, das ist der springende Punkt. Die Delphi-Welt ist für mich immer noch relativ neu, das scheint sich ja wirklich eine gängige Methode zu sein. Da möchte ich auch nichts gegen sagen, aber in meinem Projekt ist das absolut keine Option. Es darf nicht sein, dass irgendwo vielleicht doch noch bis an den Benutzer irgendeine Systemmeldung "Datenbanktreiber versagt beim Zugriff auf XYZ: Fehlercode 723" durchgereicht wird.

Lemmy 6. Jun 2013 12:11

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1217629)

Dass auf meine generelle Frage http://www.delphipraxis.net/173885-w...exception.html keine eindeutige Antwort existiert ist natürlich verständlich.

ich habe dir da 2 Links angehängt, vielleicht helfen die dir weiter, oder ggf. kennst Du die schon.

Zitat:

Zitat von Der schöne Günther (Beitrag 1217641)
Zitat:

Zitat von sahimba (Beitrag 1217640)
Im Zweifel lässt man sie eben ganz nach oben bubbeln.

Ja, das ist der springende Punkt. Die Delphi-Welt ist für mich immer noch relativ neu, das scheint sich ja wirklich eine gängige Methode zu sein.

Was ist denn die Alternative? Du reagierst auf Exceptions wenn:
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
...

sx2008 6. Jun 2013 12:20

AW: Dokumentation und Exceptions - Wann muss ich was erwarten?
 
Also ein Code wie dieser macht überhaupt keinen Sinn:
Delphi-Quellcode:
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;
1. Problem:
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:
ShowMessage
vernichtet wichtige Informationen zur Fehlersuche.
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:
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;
2. Regel:
nicht Exceptions abfangen und irgendwas mit
Delphi-Quellcode:
ShowMessage
anzeigen.
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:
raise
wesentlich öfters verwenden als
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:
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;
Selbst bei einer Zugriffsverletzung innerhalb von CalcTaxInternal() bekommt der Benutzer und der Programmierer so eine gute Info wo das Problem ist.
Die eigentliche Steuerberechnung bleibt völlig frei von störenden
Delphi-Quellcode:
try ... except
.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:38 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz