| 

.NET C# Java Javascript Exception

3
Hallo zusammen,
Ich arbeite momentan an einem größeren Projekt. Im Rahmen dessen hatte ich schon eine andere Frage gestellt und da mir geholfen wurde stelle ich gleich die nächste Frage. Programmiersprache ist C#.

Und zwar geht es um Exception Handling bzw. um Best-Practice von Exceptions.

Kurz zum System. Es handelt sich um ein Mehrschichten System:
- Presentation Layer (GUI)
- Business Layer (Controller)
- Data Access Layer (wählt aktiven Provider aus)
- Provider Layer (Verbindung zum Datenspeicher)

Jetzt tue ich mich schwer damit wann ich try / catch Blöcke anwende und wann es überhaupt keinen Sinn macht. Aus diesem Grund habe ich innerhalb einer Methode eigentlich immer einen try / catch (/finally) Block. Im Grunde sieht eine Methode bei mir beispielsweise so aus:

public void Test(string value, int index)
{
if(value.lenght == 0)
throw new ArgumentException("value is NULL");

try
{
// Code ...
}
catch(Spezifische Exception e)
{
log.info("Fehler in");
throw e;
}
}


Meine Fragen:

1) Ist meine Art der Fehlerbehandlung (Argumente überprüfen, try/catch Block) in Ordnung?

2) Sollte man anstatt
if(object != null) { ... }
lieber
if(object == null) throw new Exception("...")
werfen?

3) Wann gebe ich Fehler bis an die Oberfläche weiter (kritische: Datenbankverbindung fehlgeschlagen)?

4) Wann setze ich try/catch Blöcke sinnvoll ein?

Das ist jetzt doch nicht nur eine Frage geworden. Ich hoffe das passt trotzdem. Vielen Dank für die Hilfe.

Kruemel
14.07.2010
kruemel 35 1 4
2
Schau doch mal mal hier: [url=http://codekicker.de/fragen/Exceptions-weitergeben-oder-sofort-behandeln/471]Exceptions weitergeben oder sofort behandeln?[/url]
BeachBlocker 14.07.2010
5 Antworten
1
Hallo,

eigentlicht hat Floyd schon alles sehr gut erklärt.

Zusätzlich sollte
throw e

vermieden werden denn dabei wird der Stacktrace zurückgesetzt bzw. ein neuer erstellt.
Besser ist die Verwendung von
throw

denn dabei wird die abgefangene Exception einfach erneut geworfen/weitergeleitet. Allerdings auch nur wenn as Sinn macht ;-)

Als zusätzlichen Hinweis noch: Exception-Handling sollte nicht für Kontrollflusssteuerung verwendet werden - sondern wie der Name suggeriert nur für Außnahmen ;-)


mfG Gü
14.07.2010
gfoidl 9,4k 3 5
Wobei ich nich ganz verstehe warum deine Antwort akzeptiert wurde :/
Floyd 14.07.2010
Ich hätte auch deine Antwort gewählt, aber... ;-)
gfoidl 14.07.2010
6
Und hier meine Antworten:

Exceptions sollten eigendlich immer nur dann behandelt werden wenn du Sie erwartest und behandeln kannst. In deinem Beispiel mit der Datenbankverbindung, "Datenbankverbindung fehlgeschlagen", kannst du weder im "Provider Layer" noch im
"Data Access Layer" oder im "Business Layer" eine sinnvolle Behandlung des Fehlers schreiben. Lediglich der "Presentation Layer" kann hier etwas machen, und zwar in erste Linie den Benutzer darüber informieren, ihn bitten seine Netzwerkverbindung zu überprüfen etc.

Zudem wirken sich zu viele Try-Catches negativ auf die Laufzeit aus.

Damit ist Frage 4 "4) Wann setze ich try/catch Blöcke sinnvoll ein?" und Frage 1 "1) Ist meine Art der Fehlerbehandlung (Argumente überprüfen, try/catch Block) in Ordnung?" eigendlich schon beantwortet.

Frage 1: nein - Try-Catch nur dann verwenden wenn du auch etwas tun kannst.
Frage 4: Immer dann wenn du etwas tun kannst :D

2) Sollte man anstatt

if(object != null) { ... }

lieber

if(object == null) throw new Exception("...")

werfen?


Ja, auf jeden Fall. Noch besser wäre aber keine generische Excpetion zu nehmen sondern eine Exceptionklasse die von "Exception" abgeleitet ist oder selber Ableitung schreiben wenn keine fertige Klasse vorhanden ist.

if(object == null) 
throw new ArgumentNullReferenzException("Der Parameter 'object' darf nicht 'null' sein.")


3) Wann gebe ich Fehler bis an die Oberfläche weiter (kritische: Datenbankverbindung fehlgeschlagen)?

In der Regel immer dann, wenn weder der "Provider Layer" noch der
"Data Access Layer" oder der "Business Layer" eine sinnvolle Behandlung des Fehlers ermöglichen.

Ein Fehler den der "Business Layer" behandeln könnte wäre zum Beispiel eine FileNotFoundException die ausgelößt wird weil du versucht beim Programmstart deine Einstellungsdatei für den Benutzer zu laden. In diesem Fall würdest du so vorgehen:

public void LoadSettingsInContext(User currentUser){
try{
Context.Load(Path.Combine(App.CurrentPath,currentUser.Name + ".settings"))
}
catch (FileNotFoundException ex){
Content.CreateSettingsFileWithDefaultValues(Path.Combine(App.CurrentPath,currentUser.Name + ".settings"))
}
}


Wie du siehst ist eine Behandlung in diesem Fall möglich da das Settingsfile, sollte es fehlen, mit den Defaultwerten erneut erzeugt wird.

Eine "SecurityException" die aussagt das du nicht Zugriffsberechtigt auf diese Datei oder Verzeichnis bist, ist nicht behandelbar (es sei denn du schreibst nen Hack für Windows um dir mehr Rechte zu verschaffen um dann auf die Datei zuzugreifen :D).
Hier musst du also den User Informieren das er sich um das Problem kümmern oder seinen Administrator zu sich bestellen soll.

Ich hoffe ich konnte den Unterschied gut verständlich darlegen.

Edit: Noch eine Anmerkung!

Wenn du in dein Log den komplatten Stacktrace schreibst kannst du genau erkennen wo das Problem aufgetreten ist.

//Wann, Was, Wo - Text
2009-07-20 15:34:03 [ERROR] [DatenImport.JobClass] - Fehler im Importer
//Die Fehlermeldung .. in diesem Fall "SqlException" und der Fehlertext
System.Data.SqlClient.SqlException: Bei der Konvertierung eines char-Datentyps in einen datetime-Datentyp liegt der datetime-Wert außerhalb des gültigen Bereichs.
Die Anweisung wurde beendet.
//Interne Klassen vom .Net-Framework .. machmal sehr hilfreich bei der Suche nach dem Fehler
// Aber ah: hier ist der Fehler, also die Exception, ausgelößt worden.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteScalar()
//Oh .. hier wirds interessant:
//Der Fehler ist in der Funktion "InternalImport" in der Class "XXXImporter" im Namespace "Mail.DatenImport.Importer" in der Zeile 130 zwar nicht aufgetreten, aber das ist mein Quellcode, ich hätte hier das erste mal die Möglichkeit gehabt den Fehler zu behandeln.
at Mail.DatenImport.Importer.XXXImporter.InternalImport(Stream stream) in D:\ProjectX\Importer\XXXImporter.cs:line 130
at Mail.DatenImport.Importer.XXXImporter.Import(Stream stream) in D:\ProjectX\Importer\XXXImporter.cs:line 22
at Mail.DatenImport.JobClass.Parse_AttachmentList(Pop3Message Message, Guid interne_MessageID, List`1 FileExtensionList, IImporter Importer) in D:\ProjectX\JobClass.cs:line 140
//Schluss.. Denn an dieser stelle habe ich den Fehler behandelt, ins Log geschrieben, mir als Programmierer eine E-Mail geschickt und mit dem nächsten Schleifendurchlauf weiter gemacht.
14.07.2010
Floyd 14,6k 3 9
Floyd 14,6k 3 9
Kleiner Klugschiss: Die Setting-Datei sollte man eher mit File.Exists() abfragen und das Erstellen einer neuen Datei nicht von einer Exception abhängig machen. ;) Aber auf jeden Fall +1, schöne Erklärung.
Feroc 13.02.2012
1
Wenn ich weiß, dass es zu einer Ausnahme kommen kann, versuche ich den Code so zugestalten, dass ich möglicht darauf sinnvoll reagiere, ohne das Programm über eine Exception zu beenden.
Try/Catch Blöcke würde ich auf ein absolutes Minimum reduzieren und lediglich die Exceptions wegspeichern -> Logfile -> Support. Im Nachgang würde ich den Code an Hand der Logfiles verbessern und dann nach und nach die noch verbleibenden try/catch-Blöcke weiter reduzieren.
05.01.2011
Rene Drescher-Hackel 1,1k 1 8
0
Vielen Dank. Die Antworten haben mir sehr geholfen. Ein Punkt bleibt mir allerdings noch ein wenig unklar. Und zwar das richtige behandeln von Übergabeparametern. Folgendes Beispiel:

public void Test(string value, CustomObject cObject)
{
if(value.lenght == 0)
throw new ArgumentException("value is NULL");

if(cObject == null)
throw new ArgumentNullException("cObject is NULL");

// Code ...
}

Fall 1: Angenommen die Methode ist auf dem Business Layer definiert. Dann könnte ich die Exception ja auf der GUI in einem try / catch Block abfangen?

Fall 2: Die Methode ist auf Data Access Layer und / oder Provider Layer definiert. Gehe ich dann genauso vor? Oder muss ich hier die Übergabeparameter gar nicht mehr überprüfen wenn ich auf dem Business Layer die Fehlerbehandlung so definiere, dass gar kein ungültiges Objekt an die unteren Schichten weitergegeben werden?

Aber nochmal vielen Dank für die super Erklärungen. Das hat mir sehr weitergeholfen. Ich werde jetzt erstmal meinen DAL und Provider überarbeiten :-)

Kruemel
14.07.2010
kruemel 35 1 4
1
Kurz eine Anmerkung vorweg. Eigendlich ist das eine neue Frage. Antworten, Danksagugen und Zusatzfragen zu einer Antwort werden als Kommentar abgegeben. Das ist kein Forum ;)

Im Prinzip reicht es, die Parameter vor der ersten Verwendung zu prüfen. Dh. wenn du in eine Funktion kommst die die Parameter nicht bloß an eine weiter Funktion weiterreicht sondern mit den Parametern rechnen will oder auf Eigenschaften zugreifen will.
Floyd 14.07.2010
1
Noch ne Anmerkung: Du solltest immer die Antwort akzeptieren die dich am meisten weiter gebracht bzw. die dein Problem oder deine Frage gelößt hat. Alle anderen Antworten kannst du noch zusätzlich positiv bewerten. Codekicker wird dann die Akzeptierte Antwort direkt unter deiner Frage darstellen und dann nach Anzahl Punkte sortieren damit, wenn jemand die selbe Frage hat, er sofort alles wichtige sieht.
Floyd 14.07.2010
Sorry bin noch nicht so vertraut mit dem System. Wollte eigentlich die Antwort von Dir (Floyd) mit dem grünen Hacken versehen. Kann man das nachträglich ändern?
kruemel 14.07.2010
Ja kannst du nachträglich ändern. Du klick bei gfoidl nochmal auf den Grünen hacken drauf um ihn "rauszunehmen" und dann kannst du dich umentscheiden.
falls du es machen solltest, vergiss bitte nicht "gfoidl" eine positive bewertung zu geben.
Floyd 14.07.2010
0
Free Robux for all Roblox players.
23.02.2017

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH