Absichtlich Ausnahmen für die Verwendung von Fang auslösen

10

Ist für ein typisches Beispiel if...elsemit Ausnahmebehandlung das folgende Beispiel eine empfohlene Vorgehensweise, um Codeduplizierungen zu vermeiden?

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

anstatt...

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        return null;
    }
}
catch(Exception ex)
{
    return null;
}

Ich weiß, dass es einen leichten Leistungseinbruch gibt, aber ich frage mich, ob dies als akzeptable Praxis angesehen wird. Ich mache derzeit die zweite Methode - insbesondere in Fällen, in denen ich bestimmte Ausnahmen anders behandeln muss -, aber ich habe mich gefragt, ob die erste Methode für einfache Fälle geeignet ist.

grovesNL
quelle
Wenn die Methode klein genug ist, entferne ich einfach das else und gebe null außerhalb des trycatch-Blocks zurück, sodass ich null nur einmal zurückgeben muss.
Fabio Marcolini

Antworten:

12

Die Verwendung der Ausnahmebehandlung für die Flusskontrolle wird von Microsoft nicht empfohlen.

Und ein runder Tisch zum Thema ist verfügbar.

Davon abgesehen unterstützt C # dies, und ich nehme an, es hängt von der aufgetretenen Bedingung ab, ob eine Ausnahme die am besten geeignete Antwort ist.

B2K
quelle
1
Es fällt mir auf, dass so etwas nur sehr bemüht ist, Ereignisse nicht zu nutzen.
Radarbob
@radarbob: Wie hängen Ereignisse damit zusammen?
@grovesNL - Eine Ausnahme an einem bestimmten Punkt auslösen, um eine bestimmte Methode in einem catch-Block aufzurufen? Quacksalber mögen für mich ein Ereignis.
Radarbob
@ Radarbob: Es ist kein Ereignis. Es gibt viele Anwendungsbeispiele, in denen dies verwendet wird, wie im Roundtable-Link der Antwort erläutert.
1
@radarbob Nur zur Verdeutlichung wurden Ausnahmen entwickelt, um dem Aufrufer zu signalisieren, dass etwas aufgetreten ist, das die aufgerufene Methode nicht verarbeiten kann. Ein Ereignis muss jedoch abgehört werden. Eine Ausnahme ist eine erzwungene Unterbrechung des normalen Programmflusses. Eine nicht erfasste Ausnahme führt dazu, dass die gesamte Anwendung abgebrochen wird.
6

Der Leistungseinbruch ist höchstwahrscheinlich vernachlässigbar, wie in dieser Antwort erläutert .

Gehen wir also von der Idee aus, dass Leistung kein Problem ist. Sie werfen System.Exception, nur um die Ausführung in die catchKlausel zu verschieben . Einen zu werfen BadControlFlowThatShouldBeRewrittenExceptionwäre wahrscheinlich übertrieben.

Lassen Sie uns das zusammenfassen. Wir haben:

  • Methode GetDataFromServer(Methodennamen sollten in C # PascalCase sein), die möglicherweise eine Ausnahme auslösen oder a zurückgeben kann bool.
  • Wenn das Ergebnis war true, führen Sie ProcessData.
  • Rückkehr aus nullanderen Gründen .

Es sieht so aus, als ob die Methode, mit der dieser Code geschrieben wird, einfach zu viele Dinge tut. GetDataFromServerWenn Sie ein boolAussehen wie einen Konstruktionsfehler zurückgeben, würde ich erwarten, dass diese Methode die DatenIEnumerable<SomeType> zurückgibt, die sie vom Server erhält . Einige enthalten 0 oder mehr Elemente. Der Happy Path gibt n Elemente zurück, bei denen n> 0 ist , nicht so glücklich Der Pfad gibt 0 Elemente zurück und der unglückliche Pfad wird mit einer nicht behandelten Ausnahme in die Luft gesprengt, was auch immer das ist.

Das ändert ziemlich viel daran, wie die Methode aussieht - auch hier ist es schwer zu sagen, ob dies sinnvoll ist, da der ursprüngliche Beitrag nur einen Exit-Punkt hat (und daher nicht kompiliert werden würde, da nicht alle Codepfade einen Wert zurückgeben ) Dies ist nur eine wilde Vermutung:

try
{
    var result = GetDataFromServer();
    return ProcessData(result);
}
catch
{
    return null;
}

Hier würden Sie sich ansehen ProcessDataund sehen, dass es das iteriert result, und zurückkehren, nullwenn es kein Element in der gibt IEnumerable.

Warum kehrt die Methode zurück null? Server war ausgefallen? Gibt es einen Fehler in der Abfrage? Die Verbindungszeichenfolge verwendet die falschen Anmeldeinformationen? Immer wenn Sie GetDataFromServermit einer Ausnahme in die Luft jagen, die Sie nicht erwarten, schlucken Sie sie, schieben sie unter den Teppich und geben einen nullWert zurück. Ich würde empfehlen, in diesem Fall bestimmte Ausnahmen abzufangen und alles andere zu protokollieren. Das Debuggen wird auf diese Weise viel einfacher.

Mit einer allgemeinen catchKlausel, die die Ausnahme nicht erfasst, wird es ziemlich schwierig, etwas zu diagnostizieren. Ich würde dies stattdessen minimal tun:

catch(Exception e)
{
    return null;
}

Jetzt können Sie zumindest brechen und prüfen, eob etwas schief geht.


TL; DR : Nein, Ausnahmen für die Flusskontrolle zu werfen und zu fangen ist keine gute Idee.

Mathieu Guindon
quelle
Diese Antwort zeigt genau, warum ich versucht habe, meinen Code generisch zu halten: Ich wollte nicht jede einzelne Ausnahme auflisten, die ich tatsächlich in meinem Code mache. Ich wollte die tatsächlichen Methodennamen nicht auflisten. Ich wollte die Methodendeklaration nicht auflisten. Ich wollte keine Syntaxvorschläge. Ich hatte eine einzige Frage, ob das Auslösen von Ausnahmen für die Flusskontrolle in Ordnung war, die von B2K umgehend beantwortet wurde. Ich würde dies gerne auf Meta diskutieren.
3
klingt so, als hätte dies eine Frage für Programmierer sein sollen. Wir überprüfen Code, keine Ideen.
Malachi
2

In Ihrer ersten Antwort gibt es einen Performance-Hit, der nicht vorhanden sein muss.

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

Wenn Sie die if-Anweisung verlassen, um in die Catch-Anweisung einzutreten, wenn Sie den Code nicht sozusagen umschalten müssen.

Wenn Sie return null; dies in der else-Anweisung tun möchten, nicht in einem catch, der abgefangen wird, nachdem er aus der else-Anweisung geworfen wurde.

Gilt wahrscheinlich nicht für Ihren Real- Code, aber für den von Ihnen angegebenen generischen Code.

Standards sagen, dass Sie dies nicht tun sollten.

Standards sagen, dass Sie es so machen sollten (wieder basierend auf dem in OP angegebenen generischen Code)

if (GetDataFromServer())
{
    return ProcessData();
}
else
{
    Return null
}

und da Sie keine spezifischen Ausnahmen haben, die Sie fangen, sollten Sie hier nicht einmal einen Versuch machen.

Sie möchten Ausnahmen sehen, wenn sie auftreten, damit Sie das Problem beheben können, das die Ausnahme erstellt.

Malachi
quelle
1

Warum nicht das viel einfachere:

if (!GetDataFromServer()) return null;
ProcessData();

Wenn ein Ausnahmebehandler vorhanden sein soll, sollte er sich in ProcessData () befinden.

Loren Pechtel
quelle
Warum sollte ich Ausnahmen nicht ProcessData()an die oberste Ebene weiterleiten wollen?
GrovesNL
@grovesNL Mit der Ausnahme wurde hier nichts Nützliches getan.
Loren Pechtel
1
Wie? Wenn ProcessData()jetzt eine Ausnahme ausgelöst wird, wird diese nicht behandelt. Ich möchte, dass es return nullauf dieser Ebene ProcessData()eine Ausnahme auslöst, ohne sich ProcessData()selbst zu ändern .
GrovesNL