ausnahme werfen oder bool zurückgeben
-
-com- schrieb:
beselbube schrieb:
generell wird es als schlechter stil empfunden, wenn exceptions zur kontrollfluss steuerung benutzt werden.
manchmal hat man keine andere wahl, wenn z.b. eine methode nur exceptions werfen kann und void als rückgabetypen hat.
Eine Methode wirft ja mal nicht einfach so aus Spaß eine Exception. In solchen Fällen sollte man die Fehlerquelle vor deren Aufruf abprüfen/-fangen.
Generell ist es nicht nur ein schlechter Stil mit Exceptions "zu arbeiten", sondern gerade in größeren Schleifen führt dies zu merklichen Senkung der Gesamtperformance.
-
Buccino schrieb:
-com- schrieb:
beselbube schrieb:
generell wird es als schlechter stil empfunden, wenn exceptions zur kontrollfluss steuerung benutzt werden.
manchmal hat man keine andere wahl, wenn z.b. eine methode nur exceptions werfen kann und void als rückgabetypen hat.
Eine Methode wirft ja mal nicht einfach so aus Spaß eine Exception. In solchen Fällen sollte man die Fehlerquelle vor deren Aufruf abprüfen/-fangen.
Generell ist es nicht nur ein schlechter Stil mit Exceptions "zu arbeiten", sondern gerade in größeren Schleifen führt dies zu merklichen Senkung der Gesamtperformance.
aber -com- hat schon recht, in manchen fällen ist es schwer, es anders zu machen.. bzw. häufig auch die einfachste variante
in sprachen wie python zählt es meines Wissens auch nicht als schlechter stil.. wird in dem buch, das ich habe, jedenfalls unter kontrollstrukturen eingeführt u zieht sich durchs ganze buch
-
beselbube schrieb:
aber -com- hat schon recht, in manchen fällen ist es schwer, es anders zu machen.. bzw. häufig auch die einfachste variante
Die einfachste Variante ist oft nicht gleich auch die beste. Zudem nenn mir mal solche Fälle, wo es nicht anders geht. Sowas ist mir noch nie vorgekommen.
beselbube schrieb:
in sprachen wie python zählt es meines Wissens auch nicht als schlechter stil.. wird in dem buch, das ich habe, jedenfalls unter kontrollstrukturen eingeführt u zieht sich durchs ganze buch
Ich kenne Massenhaft schlechte Bücher aus verschiedenen Sprachen. Mit Python selber kenne ich mir zwar direkt nicht aus, würde mich aber überhaupt nicht erstaunen, wenn es dort auch schlechte Bücher gibt
Grüssli
-
Ich finds gut wie MS das z.B. bei int.Parse() gelöst hat.
Wenn man weiss, dass s normalerweise immer ne gültige Zahl ist nimmt man dieses:
string s="1234"; int bla=int.Parse(s);
...welches im Fehlerfalle ne Ausnahme wirft.
und wenn man weiss, dass öfter mal ungültige Zahlen vorkommen - das also kein Ausnahmefall mehr ist - kann man dieses nehmen:
string s="1234"; int bla=0; if (int.TryParse(s, out bla)) { // Hat geklappt ;D } else { // Hat nicht geklappt ;/ }
-
Allein der Name Exception sagt doch schon das es sich um eine Ausnahme handelt, und Steuerung des Kontrollfluss ist keine Ausnahme.
bool = Normaler Kontrollfluss,
Exception = Ausnahme, unerwarteter Fehler.Thema erledigt.
-
CSL schrieb:
Allein der Name Exception sagt doch schon das es sich um eine Ausnahme handelt, und Steuerung des Kontrollfluss ist keine Ausnahme.
bool = Normaler Kontrollfluss,
Exception = Ausnahme, unerwarteter Fehler.Thema erledigt.
Kein Argument, was für ein "Thema erledigt." ausreichen würde.
Allein der Name sagt doch schon, daß Du die Kalifornische Landesbibliothek bist.
-
Dravere schrieb:
beselbube schrieb:
aber -com- hat schon recht, in manchen fällen ist es schwer, es anders zu machen.. bzw. häufig auch die einfachste variante
Die einfachste Variante ist oft nicht gleich auch die beste. Zudem nenn mir mal solche Fälle, wo es nicht anders geht. Sowas ist mir noch nie vorgekommen.
Das habe ich auch nicht behauptet
Dravere schrieb:
beselbube schrieb:
in sprachen wie python zählt es meines Wissens auch nicht als schlechter stil.. wird in dem buch, das ich habe, jedenfalls unter kontrollstrukturen eingeführt u zieht sich durchs ganze buch
Ich kenne Massenhaft schlechte Bücher aus verschiedenen Sprachen. Mit Python selber kenne ich mir zwar direkt nicht aus, würde mich aber überhaupt nicht erstaunen, wenn es dort auch schlechte Bücher gibt
GrüssliDas Buch ist eig. ziemlich cool.. und es muss ja nicht unbedingt ein schlechtes Buch sein! Vll. ist das ganze von den Python Entwicklern ja so gedacht?! (Nur ne Vermutung
)
-
Dravere schrieb:
beselbube schrieb:
aber -com- hat schon recht, in manchen fällen ist es schwer, es anders zu machen.. bzw. häufig auch die einfachste variante
Die einfachste Variante ist oft nicht gleich auch die beste. Zudem nenn mir mal solche Fälle, wo es nicht anders geht. Sowas ist mir noch nie vorgekommen.
Wenn man einen Konstruktor mit einem Fehler beenden will, muss man eine Exception werfen.
Der einzige Ausweg ist Zombie-Objekte zu erlauben, also Objekte die dem Sprachstandard nach vollständig initialisiert sind, aber "semantisch" nicht initialisiert. Also ein Objekt einer File-Klasse, das kein File "offen hat", oder ähnliches.
Was ich persönlich ziemlich übel finde.Davon abgesehen ist es Ansichtssache bzw. Definitionssache was nun "normal" ist. z.B. kann man darüber streiten, ob es "normal" ist, dass ein User Mist eingibt, der dazu führt, dass sein Request nicht verarbeitet werden kann. IMO ist es vollkommen OK in so einem Fall eine Exception zu werfen. Und zwar einfach deswegen, weil es den Code enorm vereinfacht.
-
Muss man relativ sehen.
Eine "File" Klasse sollte im ctor sich initialisieren ohne eine Überprüfung, sodass es eine Exception wirft wenn etwas nicht klappt.Aber man sollte die Klasse sowieso erst erstellen lassen sobald die User Eingabe verifiziert wurde.
Das Beispiel ist im Prinzip so:
string userString = TakeFromUser(); try { File file = new File(userString); SoSomethingWithFile(file); } catch(Exception ex) { MessageBox.Show("An Error has occur: " + ex.Message); }
vs
string userString = TakeFromUser(); if (File.Exist(userString)) { File file = new File(userString); SoSomethingWithFile(file); } else { MessageBox.Show("The file doesn't exist"); }
Das Prüft jetzt zwar nur darauf das sie Existiert, aber das kann man erweitern indem man eine FileVerify klasse die Eingabe verifizieren lässt bevor man damit arbeitet.
Man wirft eine Exception wenn ein Fehler auftrat mit dem man nicht gerechnet hat. Falsche User eingaben wird es immer geben, also ist es keine Ausnahme sondern ein normaler Kontrollfluss, die File klasse geht davon aus das die Eingaben bereits valide sind. Wenn man an einer Stelle ist wo man weiß das dort leicht Fehler auftreten können, dann sollte man sie vorher entsprechend verifizieren statt das System auffliegen zu lassen.
Microsoft selber schreibt auch:
http://msdn.microsoft.com/en-us/library/ms229014.aspx
http://msdn.microsoft.com/en-us/library/ms229030.aspxDo not use exceptions for normal flow of control, if possible. Except for system failures and operations with potential race conditions, framework designers should design APIs so that users can write code that does not throw exceptions. For example, you can provide a way to check preconditions before calling a member so that users can write code that does not throw exceptions.
Das ist genau das was ich auch schrieb mit dem FileVerify.
Einfacheren Code hat man dadurch auch, da man sofort sieht was passiert
if (FileVerify.Verify(userInput)) ohne noch bei den Catch block schauen zu müssen, und alles in try|catch Blöcken zu packen ist auch recht unschön.
-
@CSL:
Mit Exceptions nicht für Flow-Control misbrauchen ist IMO sowas gemeint:void foo() { blubb(); try { for (size_t i = 0; i < 100; i++) { for (size_t j = 0; j < 100; j++) { if (blah()) throw 0; } } } catch (int) { blobber(); } bloink(); }
Das ist böse.
Was falsche User-Eingaben angeht: warum sollte es böse/falsch/schlecht sein da Exceptions zu verwenden?
Was dein Beispiel angeht: das finde ich etwas blauäugig, realer Code sieht anders aus, ist tiefer verschachtelt, und da zeigt sich schnell warum Exceptions praktisch sind. Auch bei falschen User-Eingaben.
Exceptions nur für "unerwartete" Fehler:
void XApp::UserSaidPrintFileOnPrinter(string pathToDocument, string printerName) { try { XDocument xdox; ErrorDescription ed = xdox.TryRead(pathToDocument); // falscher pfad wäre "normal" if (!ed) { XPrinter xprn; ed = xprn.TryOpenPrinter(printerName); // falscher druckername wäre "normal" if (!ed) ed = xprn.PrintDocument(xdoc); } // "normale" fehler anzeigen if (ed) ShowErrorMessage(ed); } catch (exception const& e) { // "aussergewöhnliche" fehler - müssen auch behandelt und dem user angezeigt werden ShowErrorMessage(e); } } ErrorDescription XDocument::TryRead(string path) { if (!File::Exists(path)) return ErrorDescription(...); File f(path); return TryRead(f); // file ist vom falschen typ wäre "normal" } ErrorDescription XDocument::TryRead(File& f) { size_t l = f.GetLength(); Header h; if (f.TryRead(&h, sizeof(h)) != sizeof(h)) // file ist vom falschen typ wäre "normal" return ErrorDescription(...); if (!ValidateHeader(h)) // file ist vom falschen typ wäre "normal" return ErrorDescription(...); for (size_t i = 0; i < h.SectionCount; i++) { ErrorDescription ed = TryReadSection(f, i); // file ist vom falschen typ wäre "normal" if (ed) return ed; } return NoError(); } ErrorDescription XDocument::TryReadSection(File& f, size_t sectionNr) { // ... } ErrorDescription XPrinter::TryOpenPrinter(string name) { if (!Win32::Printer::Exists(name)) return ErrorDescription(); m_printer.reset(new Win32::Printer(name)); return ErrorDescription(); } ErrorDescription XPrinter::PrintDocument(XDocument const& xdoc) { Win32::PrinterDocumentScope ds(m_printer); // spooler error wäre "aussergewöhnlich" for (size_t i = 0; i < xdoc.GetPageCount() && (!ed); i++) { Win32::PrinterPageScope ps(m_printer); // spooler error wäre "aussergewöhnlich" XRawPageData pd(xdoc, i); // fehler beim rendern der daten wäre "aussergewöhnlich" m_printer->Write(pd.GetDataPtr(), pd.GetDataSize()); // spooler error wäre "aussergewöhnlich" } }
Mit Exceptions für alles:
void XApp::UserSaidPrintFileOnPrinter(string pathToDocument, string printerName) { try { XPrinter(printerName).PrintDocument(XDocument(pathToDocument)); } catch (exception const& e) { // "aussergewöhnliche" fehler - müssen auch behandelt und dem user angezeigt werden ShowErrorMessage(e); } } XDocument::XDocument(string path) { File f(path); Read(f); } void XDocument::Read(File& f) { Header h; f.Read(&h, sizeof(h)); if (!ValidateHeader(h)) throw FileFormatException(...); for (size_t i = 0; i < h.SectionCount; i++) ReadSection(f, i); } void XDocument::TryReadSection(File& f, size_t sectionNr) { // ... } XPrinter::XPrinter(string name) : m_printer(new Win32::Printer(name)) { } ErrorDescription XPrinter::PrintDocument(XDocument const& xdoc) { Win32::PrinterDocumentScope ds(m_printer); for (size_t i = 0; i < xdoc.GetPageCount() && (!ed); i++) { Win32::PrinterPageScope ps(m_printer); XRawPageData pd(xdoc, i); m_printer->Write(pd.GetDataPtr(), pd.GetDataSize()); } }
Und jetzt sag mir warum ich in diesem Beispiel keine Exceptions für "normale" Fehler verwenden sollte.
Ich würde es eher so sagen:
* wenn es häufig vorkommt, dass das Fehlschlagen von einer Operation *lokal* (d.h. direkt in der aufrufenden Funktion) sinnvoll behandelt werden kann, dann sollte man eine Exception-freie Variante anbieten
* wenn es häufig vorkommt, dass das Fehlschlagen von einer Operation nicht sinnvoll lokal behandelt werden kann, dann sollte man Exceptions verwenden
Die meisten Fälle, die ich bisher hatte, waren derart, dass die Behandlung des Fehlers mehrere Ebenen höher erfolgt, als die Stelle wo man den Fehler erkennt. Auch wenn der Fehler auf falsche User-Eingaben zurückzuführen ist. Daher ist es *praktisch* dort Exceptions zu verwenden.
Meine Frage wäre nun: was spricht denn dagegen?
Ein Argument welches man häufig hört, ist dass Exceptions in manchen Implementierungen langsam sind. Das stimmt, aber langsam ist relativ. Und ich kenne keine Implementierung, wo das Werfen + Fangen + Behandeln einer Exception *so* langsam ist, dass es wirklich etwas ausmacht.
p.S.:
Noch ein Argument gegen "Pre-Validation": der sicherste (und auch einfachste) Weg, zu sehen ob etwas geht (gehen wird), ist meist es einfach zu versuchen. Bei Pre-Validation kann man einfach zu viele Dinge übersehen. Ich kann z.B. fragen ob ein File existiert. Dass ich es "sehe", heisst aber noch lange nicht, dass ich es auch lesen darf/kann. Bzw. könnte es sogar sein, dass ich *nicht* feststellen kann dass es existiert, es aber trotzdem mit dem richtigen Pfad öffnen könnte. (Je nachdem was das File-System für Berechtigungen verwendet, und je nachdem was für den "File::Exists()" Check verwendet wird.) D.h. es könnte sein, dass das "bessere" Programm mit Pre-Validation mich ein File nicht aufmachen lässt, obwohl es existiert und ich auch die nötigen Berechtigungen hätte. Tolle Wurst.
Mal ganz davon abgesehen, dass ein File jederzeit verschwinden kann. Nur dass es jetzt existiert, heisst nicht, dass es in 2ns auch noch existiert. Und wieso wäre das plötzliche Verschwinden von Files weniger "normal" als eine falsche User-Eingabe?EDIT: ich sehe wir sind hier im C# Forum und nicht im C++/RudP. Ändert allerdings an meiner Argumentation nichts. Beim Code möge man sich die entsprechenden C# Konstrukte denken, also using() Blöcke statt Klasse mit Destruktoren etc.