Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Exception Handling in C#

Geschrieben von: Christoph Wille
Kategorie: C#

This printed page brought to you by AlphaSierraPapa

Ich nehme an, daß jeder VBScript Programmierer schon mindestens einmal die sehr eingeschränkten Möglichkeiten der Fehlerbehandlung in das Land des Pfeffers gewunschen hat - ein einfaches On Error Resume Next ist nun wirklich nicht gerade das, was man unter zeitgemäßer Fehlerbehandlung verstehen sollte. Aber man mußte damit leben, immer Err.Number auszulesen, oder zu riskieren, daß der Benutzer einen nicht abgefangenen Fehler zu Gesicht bekommt.

Die gute Nachricht ist, daß das Microsoft .NET Framework für alle Programmiersprachen ein einheitliches Fehlermangagement zur Verfügung stellt: Exceptions ("Ausnahmen"). Dadurch, daß es ein fixer Teil des Frameworks ist, können Exceptions in einer Komponente ausgelöst, und im aufrufenden Programm auch abgefangen werden - und das, wie immer im Framework, über die Grenzen der Programmiersprachen hinweg. Das bedeutet auch das Ende für die von vielen "heißgeliebten" 8000er Fehlermeldungen.

Voraussetzung um den Sourcecode dieses Artikels verwenden zu können ist eine Installation des Microsoft .NET Framework SDK's auf einem Webserver. Weiters setze ich voraus, daß der Leser bereits eine Programmiersprache beherrscht - deshalb ist die Information auch in sehr komprimierter Form gehalten.

Minimales Exception Handling

Bevor wir uns die Befehle für das Exception Handling anschauen, möchte ich ein nettes kleines Beispiel produzieren, das uns mit Exceptions versorgt. Schauen wir uns dazu ein harmlos aussehendes Programm an (requestget.aspx).

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
Response.Write(strReqItem);
%>

Wenn man dieses Programm ohne Querystring aufruft, bekommt man keine Ausgabe am Bildschirm. Übergibt man einen korrekten Querystring, wird der Wert für ReqItem ausgegeben, so zum Beispiel:

http://localhost/RequestGet.aspx?ReqItem=Hello+World

Auch wenn man sich sehr anstrengt, wird das keine Exception auslösen. Wie kommen wir also zu einer Exception? Indem wir den übergebenen Wert auf einen int umwandeln wollen (requestget_cast2int.aspx):

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
int nReqItem = Int32.Parse(strReqItem);
Response.Write(nReqItem.ToString());
%>

Die Sache hat nicht nur einen, sondern gleich 2 Haken: wenn wir keinen Querystring übergeben, hat strReqItem den Wert null - und mit einem null-Objekt macht man in der Parse Methode keinen Staat - und man bekommt folgenden Fehler:

Das ist die ArgumentNullException weil kein Wert übergeben wurde. Wenn man einen Wert übergibt, der Wert allerdings mit ToInt32 nicht auf int umgewandelt werden kann, dann steht einem die FormatException ins Haus - der Haken numero due. Auslösen kann man diesen Fehler mit folgender URL:

http://localhost/RequestGet_Cast2Int.aspx?ReqItem=NaN

So - jetzt haben wir unsere Exception. Uns bleibt sozusagen "nur noch" die Aufgabe, die Exception ordnungsgemäß zu behandeln. Und dazu stellt uns C# das try-catch-finally Konstrukt zur Verfügung. Wer C++ programmiert hat, dem wird das sehr bekannt vorkommen, da es SEH (structured exception handling) nachempfunden ist. Das Gute daran - es ist sehr einfach es anzuwenden.

Es gibt gute und schlechte Beispiele, und ich möchte jetzt ausnahmsweise ein "schlechtes" aus dem Ärmel zaubern (try-one.aspx). Das ist das minimale Beispiel, wie man Exception Handling vielleicht doch nicht implementieren soll:

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
try
{
	int nReqItem = Int32.Parse(strReqItem);s
}
// entweder catch oder finally muß nach try kommen
finally
{
}
%>

Warum ist das ein schlechtes Beispiel? Nun ja, es kompiliert und läuft zwar, der Haken ist allerdings das finally Statement: dieses wird nach try immer aufgerufen, ob eine Exception ausgelöst wurde oder nicht (dort sollte also der Code hinein, der in beiden Fällen ausgeführt werden soll). Das ist noch nicht das Schlechte an der Sache - das Problem ist, daß "niemand" die Exceptions abfängt, die wir schon diskutiert haben. Der "Niemand" ist aber ASP.NET - immer wenn eine unbehandelte Exception auftritt, bekommt man wieder die ASP.NET Fehlermeldung wie schon im vorangegangenen Bild gezeigt. Also nicht unbedingt die leise Fehlerbehandlung, die wir im Sinne hatten.

Daher sollte man anstatt des finally Statements für minimales Exception Handling doch eher zuerst zu catch greifen.

Das catch Statement

Also kümmern wir uns um die Exceptions, und zwar diesmal "leise" (try-two.aspx):

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
try
{
	int nReqItem = Int32.Parse(strReqItem);
}
catch
{
}
%>

Wenn jetzt ein Fehler auftritt, wird er geschluckt, weil unser catch Block keine Fehlermeldung ausgibt. Das, was dieses Beispiel imitiert ist die Verwendung von On Error Resume Next ohne jemals den Error Code auszulesen.

Das folgende leicht modifizierte Beispiel fängt die Exception im catch Statement ab, und der Fehlertext wird zum Benutzer geschickt - die minimale Variante dessen, was die ASP.NET Error Page unter anderem auch macht:

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
try
{
	int nReqItem = Int32.Parse(strReqItem);
}
catch(Exception e)
{
	Response.Write(e.ToString());
}
%>

Dies ist natürlich auch nicht im Sinne des Erfinders - man sollte sinnvolle Dinge mit Exceptions machen, und sei es "nur", dem Benutzer eine sinnvolle Fehlermeldung zu präsentieren, was im nächsten Abschnitt beschrieben wird.

Abfrage verschiedener Exceptions

Das Schöne am catch Statement ist, daß man es mehrmals verwenden darf - um Exceptions verschiedenen Typs unterschiedlich zu behandeln. Für unser Beispiel wäre das, zuerst zu überprüfen ob etwas übergeben wurde, danach ob die Konvertierung funktioniert hat, und schließlich werden noch etwaige andere Fehler abgefangen (try-three.aspx):

<% @Page Language="C#" %>
<%
string strReqItem = Request.QueryString.Get("ReqItem");
int nReqItem = 0;
try
{
	nReqItem = Int32.Parse(strReqItem);
}
catch(ArgumentNullException e)
{
	Response.Write("Sie müssen den Parameter <b>ReqItem</b> angeben!");
}
catch(FormatException e)
{
	Response.Write("Der Parameter <b>ReqItem</b> muß eine Zahl sein!");
}
catch(Exception e)
{
	Response.Write("Fehler aufgetreten: " + e.ToString());
}
finally
{
	Response.Write("<br>Fehler oder nicht - hier sind wir!");
}
%>

Bevor die Frage auftaucht, ob man die Reihenfolge der catch Statements umdrehen kann, möchte ich sie auch gleich beantworten - ja und nein. Man darf zwar ArgumentNullException und FormatException tauschen, allerdings kann man Exception vor keine der beiden anderen reihen. Der Grund dafür ist, daß die allgemeine Exception Klasse alle spezielleren (in unserem Fall die ersten beiden) überlagert. Allerdings wird man niemals diesen Fehler machen - der Compiler beschwert sich heftigst, wenn man die Reihenfolge falsch hat.

Wie dieses Beispiel zeigt, kann man mit Exception Handling den Sourcecode deutlich fehlertoleranter gestalten. Obwohl ich nur eine Zeile im try Block hatte, ist man natürlich auf keinerlei Limit an Code im try Block eingeschränkt. Und wie man den Fehler im Endeffekt behandelt, bleibt dem Anwendungsfall überlassen.

Schlußbemerkung

Natürlich kann man nicht nur Exceptions abfangen, sondern Exceptions selbst werfen (mit dem throw Statement). Dabei muß man nicht auf vorgefertige vom Framework zur Verügung gestellte zurückgreifen, man kann auch eigene, spezialisierte, erstellen und verwenden.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20000724.zip

Verwandte Artikel

Das using Schlüsselwort
http:/www.aspheute.com/artikel/20020318.htm
Dateien umbenennen
http:/www.aspheute.com/artikel/20020409.htm
Dateiupload mit ASP.NET
http:/www.aspheute.com/artikel/20000802.htm
Der ODBC .NET Data Provider
http:/www.aspheute.com/artikel/20020206.htm
Deutsche Personalausweisnummern verifizieren
http:/www.aspheute.com/artikel/20020507.htm
Die Hashtable Klasse
http:/www.aspheute.com/artikel/20000823.htm
Directory Browsing a la .NET
http:/www.aspheute.com/artikel/20000804.htm
Ein Touch-Utility in C#
http:/www.aspheute.com/artikel/20020226.htm
Exception Handling in Visual Basic.NET
http:/www.aspheute.com/artikel/20001024.htm
Formularbasierte Authentifizierung in fünf Minuten
http:/www.aspheute.com/artikel/20020705.htm
Index Server Abfragen per Web Service
http:/www.aspheute.com/artikel/20021107.htm
Online File Management System mit ASP.NET und C# - Teil 1
http:/www.aspheute.com/artikel/20021031.htm
On-the-fly Generierung von Graphiken
http:/www.aspheute.com/artikel/20000728.htm
Regex'en zu Assemblies kompilieren
http:/www.aspheute.com/artikel/20020902.htm
Scrapen von Webseiten
http:/www.aspheute.com/artikel/20000824.htm
Sichere Konvertierungen von Referenztypen
http:/www.aspheute.com/artikel/20001019.htm
Webseiten automatisiert scrapen, Teil 2
http:/www.aspheute.com/artikel/20010911.htm
WHOIS Abfragen a la .NET
http:/www.aspheute.com/artikel/20000825.htm

Links zu anderen Sites

Die aspDEdotnet Liste
http://www.aspgerman.com/aspgerman/listen/anmelden/aspdedotnet.asp
Presenting C#
http://www.amazon.com/exec/obidos/ASIN/0672320371/alphasierrapapa

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.