2 Stimmen

Mehrteilige Operationen in einem Abschlussblock

Nehmen wir an, Sie hatten einige Ressourcen zu bereinigen wie: Dies ist C#.

try{/*stuff*/}
catch(Exception e) {/*rollback logs etc.*/}
finally{
 if( context.Transaction != null )
  context.Transaction.Dispose();
 context.Connection.Close();
 context.Connection.Dispose();
}

Wäre es sinnvoller, dies stattdessen zu tun?

try{/*stuff*/}
catch(Exception e) {/*rollback logs etc.*/}
finally{
 try{
  if( context.Transaction != null )
   context.Transaction.Dispose();
 }catch(Exception e){/*logs*/}
 finally{
  context.Connection.Close();
  context.Connection.Dispose();
 }
}

Wenn die transaction.dispose fehlschlägt, hat zumindest die Verbindung die Möglichkeit, sich zu schließen.

12voto

Richard Punkte 103159

Wäre es sinnvoller, dies stattdessen zu tun?

Es wäre besser, wenn Sie mehrere Blöcke verwenden würden.

Erstens werden Ihre catch-Blöcke alle Ausnahmen auffressen und sind nicht erforderlich (Sie können try ... finally ohne Catches verwenden). Verwenden Sie catch nur, wenn Sie die Ausnahme behandeln (oder ihr einen Wert hinzufügen) können.

Aber besser:

using (var resA = GetMeAResourceNeedingCleanUp())
using (var resB = new AnotherResourceNeedingCleanUpn(...)) {
  // Code that might throw goes in here.
}

NB. Sobald eine Ausnahme zurückgespult wird und sich die Blöcke schließlich auflösen, führt das Auslösen einer anderen Ausnahme wahrscheinlich zu (bestenfalls) Verwirrung darüber, welche Ausnahme behandelt wird. Diese zweite Leitlinie:

NICHT Ausnahmen von Dispose-Methoden oder Finalizern auslösen. Wenn Sie es Benutzern ermöglichen müssen, Aufräumfehler zu behandeln, stellen Sie eine separate Close-Methode bereit, die ihren Fehler melden kann.

Hinweis: Die "Rahmenrichtlinien für die Gestaltung" (2 und ed) hat dies als (§9.4.1):

VERMEIDEN Auslösen einer Ausnahme innerhalb von Dispose(bool) außer bei kritischen Situationen Situationen, in denen der enthaltende Prozess beschädigt wurde (Lecks, inkonsistenter gemeinsamer Zustand, etc.).

3voto

Jon Skeet Punkte 1325502

Drei Punkte:

  • Sie müssen nicht sowohl Close als auch Dispose aufrufen
  • Es ist besser, die Transaktion in einem separaten finally-Block zu beenden, da dies verhindert, dass bei der Beendigung Ausnahmen ausgelöst werden. (Es sollte nicht oft vorkommen, aber es könnte passieren.)
  • El using Anweisung ist fast immer der sauberste Weg, Ressourcen zu entsorgen. Ich verwende dies sogar, wenn ich également einen try/catch-Block wollen, einfach weil es die idiomatische Art ist, zu sagen: "Dies verwendet eine Ressource, die am Ende des Blocks entsorgt werden soll".

Kombiniert man diese, so ergeben sich zwei Verwendungsnachweise:

using (SqlConnection conn = ...)
{
    using (Transaction trans = ...)
    {
    }
}

Wenn Sie eine übermäßige Einrückung vermeiden wollen, können Sie dies so schreiben:

using (SqlConnection conn = ...)
using (Transaction trans = ...)
{
}

0voto

Frans Bouma Punkte 8141

Warum sollte ein Dispose-Aufruf fehlschlagen? Man kann auch an einem bestimmten Punkt zu vorsichtig sein. Z.B. jede 'new' Anweisung mit einem try/catch verpacken, falls der Speicher knapp wird...

0voto

jcrossley3 Punkte 11196

Ich mag es nicht, wenn meine finally-Klauseln zu langatmig sind (oder irgendeine Klausel, was das betrifft). Ich würde Ihre Ressourcenbereinigung in eine Utility-Klasse umstrukturieren. Halten Sie alle verschachtelten try's und "if null" Bedingungen dort, so dass Sie eine bessere Wiederverwendung. Da sich Ihre Aufräumlogik nur an einer Stelle befindet, können Sie beispielsweise später leicht Ihre Meinung darüber ändern, ob Sie Dispose() wirklich aufrufen müssen.

Und was noch wichtiger ist: Ihr Anwendungscode wird viel verständlicher.

try{/*stuff*/}
catch(Exception e) {/*rollback logs etc.*/}
finally{
  Utility.cleanup(context);
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X