try / catch + using, richtige Syntax

189

Welcher:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

ODER

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}
Xaqron
quelle
7
Nur eine Anmerkung: Man sollte darauf achten, nur Ausnahmen abzufangen, die tatsächlich behandelt (korrigiert) werden können, außer zum Protokollieren oder Umschließen.
John Saunders
1
Bitte beachten Sie, dass auch der letzte }Teil der usingAnweisung eine Ausnahme auslösen kann, wie hier erinnert .
Giulio Caccin
1
Bis der Debugger (in VS) die dispose-Methode nicht aufruft, wenn Sie den ersten Codeblock verwenden. Da die using-Anweisung selbst eine Ausnahme auslösen kann, kann ich den zweiten Block verwenden, um sicherzustellen, dass die implizierte finallyMethode dispose aufgerufen wird.
ShooShoSha

Antworten:

98

Ich bevorzuge den zweiten. Kann auch Fehler in Bezug auf die Erstellung des Objekts einfangen.

Jonathan Wood
quelle
11
Ich bin mit diesem Rat nicht einverstanden. Wenn Sie erwarten, dass die Objekterstellung einen Fehler auslöst, muss die Behandlung dieser Ausnahme nach außen gehen. Wenn Sie sich fragen, wohin die Behandlung gehen soll, muss die erwartete Ausnahme etwas anderes sein - es sei denn, Sie befürworten das Abfangen einer zufälligen Ausnahme, die möglicherweise erwartet wird oder nicht, was ein klassisches Anti-Muster ist (außerhalb von a Unhandled Exception Handler des Prozesses oder Threads).
Jeffrey L Whitledge
1
@ Jeffrey: Der Ansatz, den ich beschrieben habe, hat mir gute Dienste geleistet und ich mache das schon lange. Niemand sagte etwas darüber, dass die Objekterstellung fehlschlagen sollte. Durch das Umschließen eines Vorgangs, der möglicherweise fehlschlagen könnte , in einen tryBlock, mit dem Sie eine Fehlermeldung anzeigen können, wenn etwas fehlschlägt, kann das Programm den Benutzer jetzt wiederherstellen und informieren.
Jonathan Wood
Ihre Antwort ist richtig , aber weiterhin den Vorschlag , dass die try / catch hat , dort zu sein (sofort) zu allen Zeiten.
Henk Holterman
17
Ich denke, zuerst hat man auch einen Vorteil. Betrachten Sie eine DB-Transaktion, using( DBConnection conn = DBFactory.getConnection())die im Falle einer aufgetretenen Ausnahme zurückgesetzt werden müsste. Mir scheint, dass beide ihren Platz haben.
wfoster
1
Dadurch werden auch Fehler im Zusammenhang mit der Entsorgung des Objekts abgefangen.
Ahmad Ibrahim
39

Da ein using-Block nur eine Syntaxvereinfachung eines try / finally ( MSDN ) ist, würde ich persönlich Folgendes tun, obwohl ich bezweifle, dass er sich erheblich von Ihrer zweiten Option unterscheidet:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}
chezy525
quelle
4
Warum ist das Hinzufügen eines finallyBlocks Ihrer Meinung nach der usingAnweisung vorzuziehen ?
Cody Gray
10
finallyEine usingAnweisung fügt einen Block hinzu, der ein IDisposable-Objekt bereitstellt . Persönlich mag ich dies anstelle des eingebetteten usingBlocks, weil ich denke, dass es sauberer angibt, wo alles passiert und dass alles auf der gleichen "Ebene" ist. Ich mag das auch mehr als mehrere eingebettete usingBlöcke ... aber es ist alles nur meine Präferenz.
chezy525
8
Wenn Sie viel Ausnahmebehandlung implementieren, müssen Sie wirklich Spaß am Tippen haben! Das Schlüsselwort "using" gibt es schon eine Weile und seine Bedeutung ist mir ziemlich klar. Und wenn Sie es verwenden, wird der Rest meines Codes klarer, indem Sie die Unordnung auf ein Minimum beschränken.
Jonathan Wood
2
Das ist falsch. Das Objekt muss außerhalb der tryAnweisung instanziiert werden , damit es innerhalb der finallyAnweisung angeordnet werden kann. Andernfalls wird ein Compilerfehler ausgegeben: "Verwendung der nicht zugewiesenen lokalen Variablen 'myObject'"
Steve Konves
3
Technisch wird das auch nicht kompiliert. Cannot assign null to implicitly-typed local variable;) Aber ich weiß was du meinst und würde dies persönlich dem Verschachteln eines using-Blocks vorziehen.
Connell
20

Es hängt davon ab, ob. Wenn Sie Windows Communication Foundation (WCF) verwenden, using(...) { try... }funktioniert dies nicht ordnungsgemäß, wenn sich der Proxy in der usingAnweisung im Ausnahmestatus befindet. Das Entsorgen dieses Proxys führt zu einer weiteren Ausnahme.

Persönlich glaube ich an einen minimalen Handhabungsansatz, dh nur an eine Ausnahme, die Ihnen zum Zeitpunkt der Ausführung bekannt ist. Mit anderen Worten, wenn Sie wissen, dass die Initialisierung einer Variablen in usingeine bestimmte Ausnahme auslösen kann, schließe ich sie mit ein try-catch. In ähnlicher Weise, wenn innerhalb des usingKörpers etwas passieren kann, das nicht direkt mit der Variablen in zusammenhängt using, dann verpacke ich es tryfür diese bestimmte Ausnahme mit einem anderen . Ich benutze selten Exceptionin meinen catches.

Aber ich mag IDisposable und usingobwohl ich vielleicht voreingenommen bin.

Schultz9999
quelle
19

Wenn Ihre catch-Anweisung auf die in einer using-Anweisung deklarierte Variable zugreifen muss, ist inside Ihre einzige Option.

Wenn Ihre catch-Anweisung das Objekt benötigt, auf das in der Verwendung verwiesen wird, bevor es entsorgt wird, ist inside Ihre einzige Option.

Wenn Ihre catch-Anweisung eine Aktion von unbekannter Dauer ausführt, z. B. das Anzeigen einer Nachricht an den Benutzer, und Sie Ihre Ressourcen vorher entsorgen möchten, ist außerhalb Ihre beste Option.

Immer wenn ich ein ähnliches Szenario habe, befindet sich der Try-Catch-Block normalerweise in einer anderen Methode weiter oben im Aufrufstapel als die Verwendung. Es ist nicht typisch für eine Methode, zu wissen, wie mit Ausnahmen umgegangen wird, die in dieser Methode auftreten.

Meine allgemeine Empfehlung ist also draußen - weit draußen.

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Jeffrey L Whitledge
quelle
10

Beide sind gültige Syntax. Es kommt wirklich darauf an, was Sie tun möchten: Wenn Sie Fehler beim Erstellen / Entsorgen des Objekts feststellen möchten, verwenden Sie die zweite. Wenn nicht, verwenden Sie die erste.

Smashery
quelle
8

Es ist eine wichtige Sache , die ich hier aus anrufen würde: Die erste wird nicht jede Ausnahme fängt die sich aus aufrufen MyClassKonstruktor.

Madhur Ahuja
quelle
3

Ab C # 8.0 bevorzuge ich die zweite

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

und dann

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }
Reza Jenabi
quelle
1

Wenn das Objekt, das Sie im Block Using () initialisieren, möglicherweise eine Ausnahme auslöst, sollten Sie die zweite Syntax verwenden, andernfalls sind beide gleichermaßen gültig.

In meinem Szenario musste ich eine Datei öffnen und habe filePath im Konstruktor des Objekts übergeben, das ich im Using () -Block initialisiert habe, und es kann eine Ausnahme auslösen, wenn der filePath falsch / leer ist. In diesem Fall ist die zweite Syntax also sinnvoll.

Mein Beispielcode: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}
Ankur Arora
quelle
1

Ab C # 8.0 können Sie vereinfachenusing Anweisungen unter bestimmten Bedingungen , um den verschachtelten Block zu entfernen. Dies gilt dann nur für den umschließenden Block.

So können Ihre beiden Beispiele reduziert werden auf:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Und:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Beide sind ziemlich klar; Dies reduziert die Auswahl zwischen beiden auf die Frage, wie der Umfang des Objekts aussehen soll, wo Instanziierungsfehler behandelt werden sollen und wann Sie es entsorgen möchten.

Jason C.
quelle