Frage

Ich bin schon oft auf die folgende Art von Code gestoßen und frage mich, ob dies eine gute Vorgehensweise (aus Sicht der Leistung) ist oder nicht:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);
}

Grundsätzlich besteht die Aufgabe des Codierers darin, die Ausnahme in eine benutzerdefinierte Ausnahme einzuschließen und diese erneut auszulösen.

Wie unterscheidet sich die Leistung von den folgenden beiden:

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw ex;
}

oder

try
{
    ... // some code
}
catch (Exception ex)
{
    .. // Do something
    throw;
}

Gibt es einen Leistungsunterschied zwischen den drei Ansätzen, wenn man alle funktionalen oder Codierungs-Best-Practice-Argumente beiseite lässt?

War es hilfreich?

Lösung

@Brad Tutterow

Im ersten Fall geht die Ausnahme nicht verloren, sondern wird an den Konstruktor übergeben.Im Übrigen stimme ich Ihnen jedoch zu, der zweite Ansatz ist aufgrund des Verlusts der Stapelverfolgung eine sehr schlechte Idee.Als ich mit .NET gearbeitet habe, bin ich auf viele Fälle gestoßen, in denen andere Programmierer genau das getan haben, und es hat mich unendlich frustriert, als ich die wahre Ursache einer Ausnahme herausfinden musste, nur um festzustellen, dass sie von einem riesigen Try-Block erneut ausgelöst wurde Ich habe jetzt keine Ahnung, wo das Problem seinen Ursprung hat.

Ich stimme auch Brads Bemerkung zu, dass man sich um die Leistung keine Sorgen machen sollte.Diese Art der Mikrooptimierung ist eine SCHRECKLICHE Idee.Sofern Sie nicht davon sprechen, in jeder Iteration einer for-Schleife, die über einen längeren Zeitraum ausgeführt wird, eine Ausnahme auszulösen, werden Sie bei der Verwendung Ihrer Ausnahme höchstwahrscheinlich nicht auf Leistungsprobleme stoßen.

Optimieren Sie die Leistung immer dann, wenn Sie Kennzahlen haben, die darauf hindeuten, dass Sie die Leistung optimieren MÜSSEN, und treffen Sie dann die Stellen, die nachweislich der Übeltäter sind.

Es ist viel besser, lesbaren Code mit einfachen Debugging-Funktionen zu haben (IE versteckt den Stack-Trace nicht), als etwas eine Nanosekunde schneller laufen zu lassen.

Ein letzter Hinweis zum Einschließen von Ausnahmen in eine benutzerdefinierte Ausnahme ...Dies kann ein sehr nützliches Konstrukt sein, insbesondere beim Umgang mit Benutzeroberflächen.Sie können jeden bekannten und sinnvollen Ausnahmefall in eine benutzerdefinierte Basisausnahme einschließen (oder eine, die sich von dieser Basisausnahme aus erstreckt), und dann kann die Benutzeroberfläche diese Basisausnahme einfach abfangen.Wenn die Ausnahme abgefangen wird, muss sie eine Möglichkeit bieten, dem Benutzer Informationen anzuzeigen, beispielsweise eine ReadableMessage-Eigenschaft oder etwas in dieser Richtung.Wenn also die Benutzeroberfläche eine Ausnahme übersieht, liegt dies an einem Fehler, den Sie beheben müssen, und jedes Mal, wenn eine Ausnahme auftritt, handelt es sich um eine bekannte Fehlerbedingung, die von der Benutzeroberfläche ordnungsgemäß behandelt werden kann und sollte.

Andere Tipps

Offensichtlich müssen Sie mit der Erstellung neuer Objekte (der neuen Ausnahme) den Nachteil haben, dass Sie genau wie bei jeder Codezeile, die Sie an Ihr Programm anhängen, entscheiden müssen, ob sich die bessere Kategorisierung der Ausnahmen für die zusätzliche Arbeit auszahlt.

Als Ratschlag für diese Entscheidung: Wenn Ihre neuen Objekte keine zusätzlichen Informationen über die Ausnahme enthalten, können Sie die Erstellung neuer Ausnahmen vergessen.

Unter anderen Umständen ist eine Ausnahmehierarchie jedoch für den Benutzer Ihrer Klassen sehr praktisch.Angenommen, Sie implementieren das Fassadenmuster, keines der bisher betrachteten Szenarios ist gut:

  1. Es ist nicht gut, dass Sie jede Ausnahme als Ausnahmeobjekt auslösen, weil Sie (wahrscheinlich) wertvolle Informationen verlieren
  2. Es ist nicht gut, auch nicht alle Arten von Gegenständen hochzuheben, die man fängt, weil man dadurch die Fassade nicht schaffen kann

In diesem hypothetischen Fall ist es besser, eine Hierarchie von Ausnahmeklassen zu erstellen, die Ihre Benutzer von der inneren Komplexität des Systems abstrahiert und es ihnen ermöglicht, etwas über die Art der erzeugten Ausnahme zu erfahren.

Als Anmerkung:

Ich persönlich mag die Verwendung von Ausnahmen (Hierarchien von von der Exception-Klasse abgeleiteten Klassen) zur Implementierung von Logik nicht.Wie im Fall:

try {
        // something that will raise an exception almost half the time
} catch( InsufficientFunds e) {
        // Inform the customer is broke
} catch( UnknownAccount e ) {
        // Ask for a new account number
}

Wie David gehe ich davon aus, dass der zweite und der dritte besser abschneiden.Aber würde einer der drei so schlecht abschneiden, dass er sich darüber Gedanken machen würde?Ich denke, es gibt größere Probleme als nur die Leistung, über die man sich Sorgen machen muss.

FxCop empfiehlt immer den dritten Ansatz gegenüber dem zweiten, damit der ursprüngliche Stack-Trace nicht verloren geht.

Bearbeiten:Dinge entfernt, die einfach falsch waren, und Mike war so freundlich, darauf hinzuweisen.

Tun Sie nicht:

try
{
    // some code
}
catch (Exception ex) { throw ex; }

Dadurch geht der Stack-Trace verloren.

Tun Sie stattdessen Folgendes:

try
{
    // some code
}
catch (Exception ex) { throw; }

Nur das Auslösen reicht aus. Sie müssen die Ausnahmevariable nur dann übergeben, wenn Sie möchten, dass sie die innere Ausnahme einer neuen benutzerdefinierten Ausnahme ist.

Wie andere schon gesagt haben, kommt die beste Leistung von unten, da Sie lediglich ein vorhandenes Objekt erneut werfen.Der mittlere ist am wenigsten korrekt, da er den Stapel verliert.

Ich persönlich verwende benutzerdefinierte Ausnahmen, wenn ich bestimmte Abhängigkeiten im Code entkoppeln möchte.Ich habe zum Beispiel eine Methode, die Daten aus einer XML-Datei lädt.Das kann auf viele verschiedene Arten schiefgehen.

Das Lesen von der Festplatte könnte fehlschlagen (FileIOException), der Benutzer könnte versuchen, von einem Ort darauf zuzugreifen, wo er nicht erlaubt ist (SecurityException), die Datei könnte beschädigt sein (XmlParseException) oder die Daten könnten im falschen Format vorliegen (DeserialisationException).

In diesem Fall lösen alle diese Ausnahmen eine einzelne benutzerdefinierte Ausnahme (FileOperationException) erneut aus, damit die aufrufende Klasse dies alles leichter verstehen kann. Das bedeutet, dass der Aufrufer keine Verweise auf System.IO oder System.Xml benötigt, dies aber dennoch tun kann Über eine Aufzählung und alle wichtigen Informationen können Sie darauf zugreifen, welcher Fehler aufgetreten ist.

Wie bereits erwähnt, versuchen Sie nicht, so etwas mikrozuoptimieren. Das Auslösen einer Ausnahme ist hier der langsamste Vorgang.Die beste Verbesserung besteht darin, eine Ausnahme überhaupt zu vermeiden.

public bool Load(string filepath)
{
  if (File.Exists(filepath)) //Avoid throwing by checking state
  {
    //Wrap anyways in case something changes between check and operation
    try { .... }
    catch (IOException ioFault) { .... }
    catch (OtherException otherFault) { .... }
    return true; //Inform caller of success
  }
  else { return false; } //Inform caller of failure due to state
}

Der Throw in Ihrem ersten Beispiel hat den Aufwand für die Erstellung eines neuen CustomException-Objekts zur Folge.

Das erneute Auslösen in Ihrem zweiten Beispiel löst eine Ausnahme vom Typ Exception aus.

Das erneute Auslösen in Ihrem dritten Beispiel löst eine Ausnahme desselben Typs aus, der von Ihrem „irgendeinen Code“ ausgelöst wurde.

Das zweite und dritte Beispiel verbrauchen also weniger Ressourcen.

Warten....Warum ist uns die Leistung wichtig, wenn eine Ausnahme ausgelöst wird?Es sei denn, wir verwenden Ausnahmen als Teil des normalen Anwendungsablaufs (was im Widerspruch zu Best Practices steht).

Ich habe Leistungsanforderungen nur in Bezug auf Erfolg gesehen, aber nie in Bezug auf Misserfolg.

Rein leistungstechnisch würde ich vermuten, dass der dritte Fall am leistungsstärksten ist.Die anderen beiden müssen einen Stack-Trace extrahieren und neue Objekte erstellen, was beides möglicherweise ziemlich zeitaufwändig ist.

Allerdings haben diese drei Codeblöcke sehr Es gibt verschiedene (externe) Verhaltensweisen. Wenn man sie also vergleicht, fragt man sich, ob QuickSort effizienter ist als das Hinzufügen eines Elements zu einem rot-schwarzen Baum.Es ist nicht so wichtig wie die Auswahl der richtigen Vorgehensweise.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top