Verwenden Sie einen 'try-finally'-Block ohne' catch'-Block

77

Gibt es Situationen, in denen es angebracht ist, einen try-finallyBlock ohne catchBlock zu verwenden?

mkus
quelle
1
Siehe MSDN unter try-finally (C # -Referenz) . Beachten Sie, dass sich der Artikel auf die kombinierte Verwendung von tryund finallyals " try-finally- Anweisung " bezieht .
DavidRR

Antworten:

118

Sie würden es verwenden, um sicherzustellen, dass einige Aktionen nach dem tryInhalt oder einer Ausnahme ausgeführt werden, aber wenn Sie diese Ausnahme nicht verwenden möchten.

Nur um klar zu sein, dies verbirgt keine Ausnahmen. Der finallyBlock wird ausgeführt, bevor die Ausnahme über den Aufrufstapel weitergegeben wird.

Sie würden es auch versehentlich verwenden, wenn Sie das usingSchlüsselwort verwenden, da dies zu einem kompiliert wird try-finally(keine exakte Konvertierung, aber aus Gründen der Argumentation ist es nah genug).

try
{
    TrySomeCodeThatMightException();
}
finally
{
    CleanupEvenOnFailure();
}

Es finallywird nicht garantiert, dass Code ausgeführt wird, aber der Fall, in dem er nicht garantiert wird, ist ziemlich randvoll - ich kann mich nicht einmal daran erinnern. Alles, woran ich mich erinnere, ist, wenn Sie in diesem Fall sind, stehen die Chancen sehr gut, dass finallyes nicht Ihr größtes Problem ist , nicht zu laufen :-) Schwitzen Sie also im Grunde nicht.

Update von Tobias: finally Wird nicht ausgeführt, wenn der Prozess beendet wird.

Update von Paddy: Bedingungen, unter denen es schließlich nicht in einem .net try..finally-Block ausgeführt wird

Das häufigste Beispiel, das Sie möglicherweise sehen, ist das Entsorgen einer Datenbankverbindung oder einer externen Ressource, selbst wenn der Code fehlschlägt:

using (var conn = new SqlConnection("")) // Ignore the fact we likely use ORM ;-)
{
    // Do stuff.
}

Kompiliert zu so etwas wie:

SqlConnection conn;

try
{
    conn = new SqlConnection("");
    // Do stuff.
}
finally
{
    if (conn != null)
        conn.Dispose();
}
Adam Houldsworth
quelle
14
@AnthonyBlake Die Ausnahme ist nicht ausgeblendet. Wenn eine Ausnahme auftritt, wird die Ausnahme ausgeführt und die Ausnahme dann im Aufrufstapel weitergegeben.
Adam Houldsworth
2
Was den Randfall betrifft, wird der Vorgang schließlich nicht ausgeführt, wenn der Prozess beendet wird (z. B. mit "Prozess beenden" im Task-Manager oder aufgrund eines Stromausfalls).
Nuffin
1
Weitere Informationen hier darüber, wann eine endgültige Anweisung nicht ausgelöst
Paddy
1
Ein weiterer offensichtlicher Fall, in dem nicht alle final ausgeführt werden, ist, wenn mitten im finally-Block ein Fehler auftritt.
Ben Hocking
2
Schließlich wird nicht in drei Szenarien aufgerufen, die ich mir vorstellen kann: Beenden des Prozesses, Stapelüberlauf und zu wenig Speicher. All dies stoppt im Grunde die Ausführung des Programms genau an dem Punkt, an dem sie angetroffen werden, und signalisiert dem Betriebssystem, den Prozess zu beenden.
KeithS
5

usingist gleichwertig try-finally. Sie werden es nur verwenden, try-finallywenn Sie innen aufräumen möchten finallyund sich nicht um die Ausnahme kümmern.

Der beste Ansatz wird sein

try
{
   using(resource)
   {
       //Do something here
   }   
}catch(Exception)
{
     //Handle Error
}

Selbst wenn die von aufgerufene Bereinigung usingfehlschlägt, schlägt Ihr Code nicht fehl.

Es gibt einige Bedingungen, unter denen finallynicht ausgeführt wird.

  • Wenn es welche gibt StackOverflowExceptionoder ExecutingEngineException.
  • Der Prozess wird von einer externen Quelle beendet.

Hoffe das beantwortet deine Zweifel.

Amar Palsapure
quelle
3

Wenn Sie beispielsweise eine nicht verwaltete Ressource haben, die Sie im try-Block erstellen und verwenden, können Sie den finally-Block verwenden, um sicherzustellen, dass Sie diese Ressource freigeben. Der finally-Block wird immer ausgeführt, unabhängig davon, was im try-Block passiert (z. B. Ausnahmen).

ZB lautet die Anweisung lock (x) wirklich:

System.Threading.Monitor.Enter(x); 
try { ... } 
finally 
{ 
    System.Threading.Monitor.Exit(x); 
} 

Der finally-Block wird immer aufgerufen, um sicherzustellen, dass die exklusive Sperre aufgehoben wird.

Jeb
quelle
3

Gute Erklärung mit Code:

void MyMethod1()
{
    try
    {
        MyMethod2();
        MyMethod3();
    }
    catch(Exception e)
    {
        //do something with the exception
    }
}


void MyMethod2()
{
    try
    {
        //perform actions that need cleaning up
    }
    finally
    {
        //clean up
    }
}


void MyMethod3()
{
    //do something
}

Wenn entweder MyMethod2 oder MyMethod3 eine Ausnahme auslöst, wird diese von MyMethod1 abgefangen. Der Code in MyMethod2 muss jedoch Bereinigungscode ausführen, z. B. das Schließen einer Datenbankverbindung, bevor die Ausnahme an MyMethod1 übergeben wird.

http://forums.asp.net/t/1092267.aspx?Try+without+Catch+but+with+finally+doesn+t+throw+error+Why+no+syntax+error+

Donstack
quelle
2
Ein Beispiel für diese 'Bereinigung' könnte saveLogFile () sein, das ich oft ganz am Ende eines Programms einfügen möchte, egal was passiert.
Vincent
1

Sie benötigen einen finally-Block, wenn unabhängig davon, welche (falls vorhanden) Ausnahmen abgefangen werden oder auch wenn keine abgefangen werden, Sie dennoch Code ausführen möchten, bevor der Block beendet wird. Beispielsweise möchten Sie möglicherweise eine geöffnete Datei schließen.

Siehe auch try-finally

Ian Norton
quelle
1

try / finally: Wenn Sie keine Ausnahmen behandeln möchten, aber sicherstellen möchten, dass einige Aktionen ausgeführt werden, unabhängig davon, ob eine Ausnahme vom aufgerufenen Code ausgelöst wird oder nicht.

Mitch Wheat
quelle
1

Ich weiß nichts über C #, aber es scheint, dass alles, was Sie mit einem try-finally tun könnten, Sie eleganter mit einer using-Anweisung tun könnten . C ++ hat aufgrund seines RAII nicht einmal ein endgültiges Ergebnis .

Neil G.
quelle
Nicht unbedingt. Was ist, wenn Sie benutzerdefinierten Code ausführen möchten, der nicht durch einfaches Entsorgen behandelt wird? Wenn Sie beispielsweise einen Zähler in a erhöhen tryund dann in a verringern finally, können Sie dies in einer usingAnweisung nicht tun .
Mark A. Donohoe
@ MarkA.Donohoe Können Sie kein Objekt erstellen, das einen Verweis auf den Zähler enthält, den es bei der Entsorgung dekrementiert?
Neil G
Ja, aber warum ein ganzes Objekt erstellen, um eine Einwegschnittstelle für die using-Anweisung zu implementieren? Das passt das Problem zur Lösung. A Try-[Catch]-Finallyerledigt all das, ohne ein solches Objekt erstellen zu müssen.
Mark A. Donohoe
@ MarkA.Donohoe Die Objektlösung ist überlegen, da sie an einer Stelle ein Klammermuster implementiert. Sie können den Dekrementierungscode nicht vergessen, verlegen oder versehentlich löschen.
Neil G
Es tut mir leid, aber ich bin anderer Meinung. Sie konzentrieren sich auf explizites dekrementierendes Verhalten, aber wer hat gesagt, dass sie perfekt aufeinander abgestimmt sein müssen? Was ist, wenn es eine andere Inkrementierungs- / Dekrementierungslogik gibt, die auch von anderen Mitgliedern in der Funktion abhängt? Außerdem führen Sie jetzt nicht nur ein neues Objekt nur für eine Verwendung ein, sondern dies auch auf dem Heap anstelle eines einfachen Int auf dem Stapel. Es gibt definitiv eine Zeit und einen Ort, den Sie nutzen können, usingaber Sie können die pauschale Aussage nicht so machen, wie Sie es getan haben. Wiederum definiert das das Problem durch die Lösung, die für mich rückwärts ist.
Mark A. Donohoe
1

Hier ist eine Situation, in der Sie try finally verwenden möchten: Wenn Sie normalerweise eine using-Anweisung verwenden würden, dies jedoch nicht können, weil Sie eine Methode durch Reflektion aufrufen.

Das wird nicht funktionieren

using (objMsg  =  Activator.CreateInstance(TypeAssist.GetTypeFromTypeName("omApp.MessagingBO")))
{

}

stattdessen verwenden

           object objMsg = null;
            try
            {
                objMsg
                   = Activator.CreateInstance(TypeAssist.GetTypeFromTypeName("myAssembly.objBO"));

                strResponse = (string)objMsg.GetType().InvokeMember("MyMethod", BindingFlags.Public
                        | BindingFlags.Instance | BindingFlags.InvokeMethod, null, objMsg,
                        new object[] { vxmlRequest.OuterXml });
            }               
            finally
            {
                if (objMsg!=null)
                    ((IDisposable)objMsg).Dispose();
            }
jazza1000
quelle
0

Hier ist ein Anwendungsfall, den ich immer (ähm ..) benutze:

int? x; //note the nullable type here!
try
{
    x = int.Parse(someString);
}
catch { } //don't care, let it just be null
Alex
quelle
0

1. Wir können den try-Block ohne catch verwenden, aber wir sollten den catch / finally-Block verwenden. 2.Wir können nicht nur try block verwenden.

Parmeshwar Karale
quelle