Step 15 - except

Step 15 - Exceptionhandling.Basiswissen

Try and except (versuche das... und ansonsten tue dies...) wird zur Fehlerbehandlung (Exeptionhandling) verwendet.  Es ist zum Beispiel möglich bei einer Benutzereingabe zu überprüfen, ob es sich tatsächlich um eine Zahl (Integer) handelt:
try                        // versuche....
  i:=StrToInt(Edit1.Text);
except                     // ansonsten tue dies...
  ShowMessage('Error: Bitte geben Sie eine ganze Zahl ein!');
end;
 
Damit könnte man z.B. den Taschenrechner aus Step 06 so verbessern, dass eine "schöne Fehlermeldung" bei einer fehlerhaften Eingabe erscheint. Der Quelltext des Taschenrechners würde dann so aussehen (zumindest für die Addition):
procedure TForm1.BtnSummeClick(Sender: TObject);
var
    a, b, c : Integer;
begin
  try                             // versuche
    a := StrToInt( EdSummand1.Text);
    b := StrToInt( EdSummand2.Text);
    c := a+b;
    EdSumme.Text := IntToStr( c);
  except                          //ansonsten  ...
    ShowMessage( 'Ihre Eingabe konnte nicht verarbeitet werden. Bitte geben Sie eine ganze Zahl ein.'); // Fehlerausgabe
  end;
end;
 
Eine weitere Form der Fehlerbehandlung lernen wir später, wenn es darum geht, reservierten Speicher im Fehlerfall wieder freizugeben.
Bei einer sauberen Programmierung geht es allerdings nicht unbedingt darum, Fehler wieder aufzufangen, sondern sie besser von vornherein zu vermeiden. Bei unserem Beispiel ginge das durch einen kleinen Algorithmus, der uns hilft, eine Zahl von einem String zu unterscheiden. D.h. Eine Funktion, welche wahr zurückliefert, wenn ein String in eine Zahl umgewandelt werden kann, oder eben falsch, wenn dies nicht gelingt. So kann man sogar genauer sagen woran es liegt, dass die Eingabe nicht als Zahl erkannt wird, was dann sogar noch benutzerfreundlicher ist. Jedoch kommt man nicht immer um die Fehlerbehandlung drumherum, denn es gilt immer den DAU mit einzuplanen - und sollte diese Person das benutzen, muss es nicht nur einfach bedienbar, sondern bombensicher sein ;). Aber okay der eigentliche Grund ist meistens, dass einfach nicht genug Zeit dafür da ist, um wirklich alle Fehlerquellen von Vornherein auszuschließen.
Will man jedoch genauer differenzieren, welche Art von Fehler aufgetreten ist, dann kann man das durch Fehlerklassen machen:
  try                             // versuche
    a := StrToInt( EdDivident.Text);
    b := StrToInt( EdDivisor.Text);
    c := a div b;
    EdQuotient.Text := IntToStr( c);
  except                          // ansonsten

    on ex:EDivByZero do           // bei Division durch 0

      ShowMessage('Division durch 0!?! Exception-Meldung:' + ex.Message);

    on ex:EIntOverflow do         // bei Integer Überlauf

      ShowMessage('Integer überlauf!?! Exception-Meldung:' + ex.Message);

    on ex:EIntfCastError do       // bei einem Casting-Ausnahmen

      ShowMessage('Integer Cast Fehler?!? Exception-Meldung:' + ex.Message);

    // ... die Aufzählung kann beliebig erweitert werden ...
    
    on ex:Exception do               // bei allen anderen Ausnahmen

      // alternativ, falls wir auf ex nicht zugreifen können müssen,
      // können wir für "alle anderen Ausnahmen" auch "else" verwenden,
      // wie bei der if-Anweisung

      // Mehr als eine Zeile? => Umschließe mit begin...end;
      begin
        msg := 'Unbekannter Fehler:' + ex.ClassName + sLineBreak;
        msg := msg + 'a = ' + IntToStr(b) + sLineBreak;
        msg := msg + 'b = ' + IntToStr(b) + sLineBreak;
        msg := msg + 'c = ' + IntToStr(c);
        ShowMessage( msg);
      end;

  end; // von try...except
Hierbei wird unterschieden zwischen Division durch 0 und dem Rest an möglichen Fehlern, was in diesem Beispielcode im Grunde nichtsmehr sein kann. Interessant bei dieser Struktur ist allemal, dass vor dem else und davor vor jedem folge-"on" ein Semikolon ist. Die Struktur ist also sehr mit der einer Case vergleichbar. case Errortype of 1 : ....