EDIT 20.11.2009 :
Ich habe gerade diesen MSDN-Artikel über die Verbesserung der Leistung von verwaltetem Code gelesen und dieser Teil hat mich an diese Frage erinnert:
Die Leistungskosten für das Auslösen einer Ausnahme sind erheblich. Obwohl die strukturierte Ausnahmebehandlung die empfohlene Methode zur Behandlung von Fehlerbedingungen ist, stellen Sie sicher, dass Sie Ausnahmen nur in Ausnahmefällen verwenden, wenn Fehlerbedingungen auftreten. Verwenden Sie keine Ausnahmen für den regulären Kontrollfluss.
Dies gilt natürlich nur für .NET und richtet sich auch speziell an diejenigen, die Hochleistungsanwendungen entwickeln (wie ich). Es ist also offensichtlich keine universelle Wahrheit. Trotzdem gibt es viele von uns .NET-Entwicklern, daher war es meiner Meinung nach erwähnenswert.
EDIT :
OK, zunächst einmal wollen wir eines klarstellen: Ich habe nicht die Absicht, mich mit irgendjemandem über die Leistungsfrage zu streiten. Im Allgemeinen neige ich dazu, denen zuzustimmen, die vorzeitige Optimierung für eine Sünde halten. Lassen Sie mich jedoch nur zwei Punkte ansprechen:
Das Poster fordert eine objektive Begründung für die konventionelle Weisheit, dass Ausnahmen sparsam verwendet werden sollten. Wir können die Lesbarkeit und das richtige Design besprechen, was wir wollen. Aber dies sind subjektive Angelegenheiten mit Menschen, die bereit sind, auf beiden Seiten zu streiten. Ich denke, das Plakat ist sich dessen bewusst. Tatsache ist, dass die Verwendung von Ausnahmen zur Steuerung des Programmflusses häufig eine ineffiziente Vorgehensweise ist. Nein, nicht immer , aber oft . Aus diesem Grund ist es vernünftig, Ausnahmen sparsam zu verwenden, genauso wie es ein guter Rat ist, rotes Fleisch zu essen oder Wein sparsam zu trinken.
Es gibt einen Unterschied zwischen der Optimierung ohne guten Grund und dem Schreiben von effizientem Code. Die Folge davon ist, dass es einen Unterschied gibt zwischen dem Schreiben von etwas Robustem, wenn nicht Optimiertem, und etwas, das einfach ineffizient ist. Manchmal denke ich, wenn Leute über Dinge wie Ausnahmebehandlung streiten, reden sie wirklich nur aneinander vorbei, weil sie grundlegend unterschiedliche Dinge diskutieren.
Betrachten Sie zur Veranschaulichung meines Standpunkts die folgenden C # -Codebeispiele.
Beispiel 1: Erkennen ungültiger Benutzereingaben
Dies ist ein Beispiel dafür , was ich Ausnahme nennen würde Missbrauch .
int value = -1;
string input = GetInput();
bool inputChecksOut = false;
while (!inputChecksOut) {
try {
value = int.Parse(input);
inputChecksOut = true;
} catch (FormatException) {
input = GetInput();
}
}
Dieser Code ist für mich lächerlich. Natürlich funktioniert es . Niemand argumentiert damit. Aber es sollte so etwas sein wie:
int value = -1;
string input = GetInput();
while (!int.TryParse(input, out value)) {
input = GetInput();
}
Beispiel 2: Überprüfen, ob eine Datei vorhanden ist
Ich denke, dieses Szenario ist tatsächlich sehr verbreitet. Es scheint für viele Leute viel "akzeptabler" zu sein, da es sich um Datei-E / A handelt:
string text = null;
string path = GetInput();
bool inputChecksOut = false;
while (!inputChecksOut) {
try {
using (FileStream fs = new FileStream(path, FileMode.Open)) {
using (StreamReader sr = new StreamReader(fs)) {
text = sr.ReadToEnd();
}
}
inputChecksOut = true;
} catch (FileNotFoundException) {
path = GetInput();
}
}
Das scheint vernünftig genug, oder? Wir versuchen eine Datei zu öffnen. Wenn es nicht da ist, fangen wir diese Ausnahme ab und versuchen, eine andere Datei zu öffnen ... Was ist daran falsch?
Nichts wirklich. Aber betrachten Sie diese Alternative, die nicht nicht alle Ausnahmen werfen:
string text = null;
string path = GetInput();
while (!File.Exists(path)) path = GetInput();
using (FileStream fs = new FileStream(path, FileMode.Open)) {
using (StreamReader sr = new StreamReader(fs)) {
text = sr.ReadToEnd();
}
}
Wenn die Leistung dieser beiden Ansätze tatsächlich gleich wäre, wäre dies natürlich nur eine Lehrfrage. Schauen wir uns das an. Für das erste Codebeispiel habe ich eine Liste mit 10000 zufälligen Zeichenfolgen erstellt, von denen keine eine richtige Ganzzahl darstellt, und dann ganz am Ende eine gültige Ganzzahlzeichenfolge hinzugefügt. Unter Verwendung der beiden oben genannten Ansätze waren dies meine Ergebnisse:
Verwendung try
/ catch
Block: 25,455 Sekunden
Verwendung int.TryParse
: 1,637 Millisekunden
Für das zweite Beispiel habe ich im Grunde das Gleiche getan: Ich habe eine Liste mit 10000 zufälligen Zeichenfolgen erstellt, von denen keine ein gültiger Pfad war, und dann ganz am Ende einen gültigen Pfad hinzugefügt. Dies waren die Ergebnisse:
Verwendung try
/ catch
Block: 29,989 Sekunden
Verwendung File.Exists
: 22,820 Millisekunden
Viele Leute antworteten darauf mit den Worten: "Ja, es ist äußerst unrealistisch, 10.000 Ausnahmen zu werfen und zu fangen. Dies übertreibt die Ergebnisse." Natürlich tut es das. Der Unterschied zwischen dem Auslösen einer Ausnahme und dem eigenen Umgang mit schlechten Eingaben wird für den Benutzer nicht erkennbar sein. Es bleibt die Tatsache, dass die Verwendung von Ausnahmen in diesen beiden Fällen 1.000- bis über 10.000-mal langsamer ist als die alternativen Ansätze, die genauso lesbar sind - wenn nicht sogar mehr.
Deshalb habe ich das folgende Beispiel für die GetNine()
Methode aufgenommen. Es ist nicht so, dass es unerträglich langsam oder inakzeptabel langsam ist; es ist, dass es langsamer ist als es sein sollte ... ohne guten Grund .
Auch dies sind nur zwei Beispiele. Von Natürlich wird es Zeiten geben , wenn die Leistungseinbußen Ausnahmen nicht mit diesem schweren (Pavel Recht, denn immerhin ist es an der Umsetzung abhängen) sind. Ich sage nur: Stellen wir uns den Tatsachen, Leute - in Fällen wie dem oben genannten ist das Werfen und Fangen einer Ausnahme analog zu GetNine()
; Es ist nur eine ineffiziente Art, etwas zu tun , das leicht besser gemacht werden könnte .
Sie fragen nach einer Begründung, als ob dies eine dieser Situationen wäre, in denen jeder auf einen Zug gesprungen ist, ohne zu wissen warum. Aber in der Tat ist die Antwort offensichtlich, und ich denke, Sie wissen es bereits. Die Ausnahmebehandlung hat eine schreckliche Leistung.
OK, vielleicht ist es in Ordnung für Ihr spezielles Geschäftsszenario, aber relativ gesehen bedeutet das Auslösen / Abfangen einer Ausnahme viel mehr Overhead als in vielen, vielen Fällen erforderlich ist. Sie wissen es, ich weiß es: die meiste Zeit , wenn Sie Ausnahmen von Steuerprogrammablauf verwenden, sind Sie nur langsam Code zu schreiben.
Sie könnten genauso gut fragen: Warum ist dieser Code schlecht?
private int GetNine() {
for (int i = 0; i < 10; i++) {
if (i == 9) return i;
}
}
Ich wette, wenn Sie diese Funktion profilieren, werden Sie feststellen, dass sie für Ihre typische Geschäftsanwendung recht akzeptabel schnell ausgeführt wird. Das ändert nichts an der Tatsache, dass es ein schrecklich ineffizienter Weg ist, etwas zu erreichen, das viel besser gemacht werden könnte.
Das ist es, was die Leute meinen, wenn sie von Ausnahme "Missbrauch" sprechen.