c # "finally" blockiert, dass nur bei Ausnahmen ausgeführt wird

7

Edit: Ich habe mir den Antwortcode angeschaut: KEINE von ihnen machen was ich will (habe ich überprüft). Es scheint, dass es keine Möglichkeit gibt, in nativen c # zu machen, was ich will. Ich denke, das ist keine Katastrophe, nur eine Schande, da .NET es unterstützt (siehe akzeptierte Antwort).

Danke allen.

Ich habe c # -Code (Teil eines Testframeworks, das niemals außer einem Debugger ausgeführt wird), der darauf hinweist, dass die Ausnahme nicht abgefangen wird, da dies das Debuggen des Codes im unbewickelten Teil des Stacks zu einem königlichen Schmerz macht .

%Vor%

ist ihre bessere Möglichkeit, dies zu tun?

  

Eine Lösung müsste sich genau so verhalten wie der Debugger im Hinblick auf nicht erfasste Ausnahmen. Es müsste zu der einzigen Ausnahme der ersten Chance führen, und der Debugger bricht an dem Punkt, an dem die Ausnahme ursprünglich ausgelöst wurde, nicht in einem catch-Block.

     

Insbesondere brauche ich den Debugger auf nicht gefangenen Ausnahmen, um eine in-Seite MightThrow zu stoppen.

Das Folgende funktioniert nicht, da der Debugger nicht an der richtigen Stelle unterbrochen werden kann

%Vor%

Und das funktioniert nicht, weil es Stapelinformationen verliert (und auch an der falschen Stelle einbricht).

%Vor%

Ich weiß, dass ich in D eine scope(failure) blockieren

    
BCS 13.01.2011, 15:05
quelle

16 Antworten

33

Also, in .NET ist das, was Sie verlangen, theoretisch möglich, aber es wird nicht einfach sein.

CIL definiert fünf Typen des Ausnahmebehandlungsblocks! Die try , catch und finally , an die du in C # gewöhnt bist, und zwei andere:

  • filter - ähnelt einem catch -Block, kann jedoch beliebigen Code ausführen, um zu bestimmen, ob er den Fehler behandeln möchte, anstatt nur nach dem Typ zu suchen. Dieser Block hat Zugriff auf das Ausnahmeobjekt und hat die gleiche Auswirkung auf die Ablaufverfolgung des Ausnahmestapels wie ein catch -Block.

  • fault - ähnelt einem finally -Block, wird jedoch nur ausgeführt, wenn eine Ausnahme auftritt. Dieser Block hat keinen Zugriff auf das Ausnahmebedingungsobjekt und hat keine Auswirkungen auf die Ausnahmestapelüberwachung (genau wie ein finally -Block).

filter ist in einigen .NET-Sprachen verfügbar (z. B. VB.NET, C ++ / CLI), ist aber leider nicht in C # verfügbar. Allerdings kenne ich keine andere Sprache als CIL, die es erlaubt, den fault -Block auszudrücken.

Weil es kann in IL gemacht werden bedeutet, dass nicht alle verloren sind. Theoretisch könnten Sie Reflection.Emit verwenden, um eine Funktion mit einem fault -Block dynamisch zu emittieren und den Code, den Sie ausführen möchten, als Lambda-Ausdrücke (dh einen für den Testteil, einen für den Fehlerteil usw.) zu übergeben ), aber (a) das ist nicht einfach, und (b) ich bin nicht davon überzeugt, dass dies Ihnen tatsächlich eine nützlichere Stapelverfolgung geben wird, als Sie gerade bekommen.

Tut mir leid, die Antwort ist keine "Hier ist wie es zu machen", aber zumindest weißt du es jetzt! Was Sie jetzt tun, ist wahrscheinlich der beste Ansatz IMHO.

Hinweis für diejenigen, die sagen, dass der in der Frage verwendete Ansatz "schlechte Praxis" ist, ist es wirklich nicht. Wenn Sie einen catch -Block implementieren, sagen Sie "Ich muss etwas mit dem Ausnahmeobjekt machen, wenn eine Ausnahme auftritt" und wenn Sie ein finally implementieren, sagen Sie: "Ich brauche das Ausnahmeobjekt nicht, aber Ich muss etwas vor dem Ende der Funktion tun ".

Wenn Sie tatsächlich sagen wollen: "Ich brauche das Ausnahme-Objekt nicht, aber ich muss etwas tun, wenn eine Ausnahme auftritt", dann befinden Sie sich in der Mitte zwischen den beiden, dh Sie möchten ein fault Block. Da dies in C # nicht zur Verfügung steht, haben Sie keine ideale Option. Sie können also auch die Option auswählen, die weniger wahrscheinlich Fehler verursacht, indem Sie vergessen, erneut zu werfen, und die Stack-Trace nicht beschädigt.

    
Greg Beech 10.02.2009, 22:12
quelle
27

Wie wäre es damit:

%Vor%

Wirklich, das ist alles, was du getan hast. Schließlich gilt für Dinge, die ausgeführt werden müssen, unabhängig davon, ob eine Ausnahme aufgetreten ist.

[ Bearbeiten: Erläuterung]

Basierend auf den Kommentaren, die Sie erwähnt haben, möchten Sie, dass die Ausnahme weiterhin geworfen wird, ohne den ursprünglichen Stack-Trace zu ändern. In diesem Fall möchten Sie den einfachen Schmuck verwenden, den ich hinzugefügt habe. Auf diese Weise kann die Ausnahme den Stapel fortsetzen und Sie können weiterhin einen Teil der Ausnahme behandeln. Typische Fälle können das Schließen von Netzwerkverbindungen oder Dateien sein.

[ Zweite Bearbeitung: Zur Klärung]

  

Ich brauche speziell den Debugger   Ungefangene Ausnahmen, um bei der   ursprünglicher Wurfpunkt (in   MightThrow) nicht im Catch-Block.

Ich würde mich dagegen aussprechen, jemals eine Best Practice zu brechen (und ja, dies ist eine Best-Practice-Methode, um Ausnahmen teilweise zu behandeln), um Ihrem Debugging einen kleinen Wert hinzuzufügen. Sie können die Ausnahmebedingung leicht untersuchen, um den Speicherort des Ausnahmeausbruchs zu ermitteln.

[ Letzte Änderung: Sie haben Ihre Antwort]

kronoz hat Ihnen nachdenklich geholfen mit der Antwort, die du gesucht hast. Best Practices nicht brechen - Visual Studio richtig verwenden! Sie können festlegen, dass Visual Studio genau bricht, wenn eine Ausnahme ausgelöst wird. Hier finden Sie offizielle Informationen zum Thema .

Ich wusste eigentlich nichts von dem Feature, also gib ihm die akzeptierte Antwort. Aber bitte, versuchen Sie nicht, Ausnahmen in irgendeiner funky Art zu behandeln, nur um sich eine Hand zu entschlüsseln. Alles, was du tust, ist, dich für mehr Bugs zu öffnen.

    
Randolpho 10.02.2009 20:12
quelle
11

Wenn Sie daran interessiert sind, dass der Debugger einfach genau stoppt, wo die Ausnahme aufgetreten ist, haben Sie dann Ausnahmen der ersten Chance in Betracht gezogen?

Wenn Sie Tools | Exceptions öffnen und dann das Feld Common Language Runtime Exceptions ankreuzen, wird der Debugger unabhängig von try / catch / finally-Blöcken an der Ausnahmestelle anhalten.

Aktualisieren : Sie können die genaue Ausnahme angeben, die Sie abfangen möchten, indem Sie den Baum [+] im Dialogfeld "Ausnahmen" erweitern. Es wird natürlich jedes Mal ausgelöst, wenn any Ausnahme des angegebenen Typs [s] auftritt [s], Sie können es jedoch auch mitten in einer Debugging-Sitzung nach Belieben ein- und ausschalten, also mit Bedacht Wenn Sie Haltepunkte verwenden, können Sie damit Ihre Gebote erfüllen. Ich benutzte es erfolgreich, um das "Ziel eines Aufrufs zu umgehen, hat eine Ausnahme ausgelöst" Ball Schmerzen aus der Verwendung von Reflexion, um Objekte instanziieren. Sehr nützliches Werkzeug in solchen Situationen. Beachten Sie auch die Locals und Stack-Trace sollte fest zur Verfügung stehen, soweit ich mich erinnere (nur einen kurzen Test und sie sind verfügbar), also keine Probleme dort.

Natürlich, wenn Sie Dinge protokollieren wollen, dann liegt das außerhalb des Bereichs eines IDE-Debuggers; und in diesem Fall werden Ausnahmen der ersten Chance Ihnen nicht helfen!

Probieren Sie es wenigstens aus; Ich fand sie sehr nützlich und sie könnten für Ihr Problem besser geeignet sein als Sie denken.

    
ljs 10.02.2009 20:38
quelle
7

Was ist los mit:

%Vor%     
Rowland Shaw 10.02.2009 20:13
quelle
7

Verwenden Sie für Code, der nur für Ausnahmen ausgeführt werden sollte, den catch-Block:

%Vor%     
M4N 10.02.2009 20:13
quelle
4

Das ist was du willst. Es wird diese Methode nur aufrufen, wenn ein Fehler auftritt, und die "throw" -Anweisung wird die Ausnahme mit intaktem Callstack erneut auslösen.

%Vor%     
Jon Tackabury 10.02.2009 20:16
quelle
3

Ein "finally" -Block, der nur bei einem Fehler ausgeführt wird, wird "catch" (ohne Parameter) genannt. : -)

Jetzt gibt es eine kleine Einschränkung. Wenn Sie einen speziellen "catch" -Fall für einen bestimmten Ausnahmetyp haben möchten und und einen generischen "catch" haben, der für alle Ausnahmen funktioniert, müssen Sie ein wenig an eine benutzerdefinierte Logik anpassen.

Also würde ich etwas tun wie:

%Vor%     
Franci Penov 10.02.2009 20:21
quelle
2

Jedes Beispiel hat bisher den ursprünglichen StackTrace gemäß meinen Tests verloren. Hier ist eine Lösung, die für Sie arbeiten sollte.

%Vor%     
Adam Lassek 10.02.2009 20:41
quelle
2

Wie wäre es mit einer Ausnahme, bei der "MightThrow" nicht ausgelöst wird? nicht ? %Vor%     

jdisk 10.02.2009 21:14
quelle
2

Lassen Sie mich Ihre Anforderungen so zusammenfassen, wie ich sie verstehe:

  1. Sie möchten Code, der nur ausgeführt wird, wenn eine Ausnahme generiert wird, um eine Protokollierung durchzuführen.
  2. Sie möchten Ihr Testframework unter Debugger ausführen und an dem Punkt brechen, an dem die Ausnahme ausgelöst wird.

Um Ihre erste Anforderung zu erfüllen, sollten Sie den Code so schreiben, wie jeder vorgeschlagen hat - mit parameterlosem Fang und Wurf.

Um Ihre zweite Anforderung zu erfüllen, während Sie den parameterlosen Catch verwenden, können Sie Ihren Debugger so konfigurieren, dass er bricht, wenn eine Ausnahme ausgelöst wird, nicht nur, wenn eine nicht behandelte Ausnahme vorliegt. Ich vermute, Sie wissen, wie es geht, aber ich werde es hier für die Vollständigkeit der Antwort setzen: in VS können Sie das in Debug tun - & gt; Ausnahme - & gt; Common Language Runtime-Ausnahmen - & gt; Aktivieren Sie das Kontrollkästchen "Geworfen".

Wenn Sie wissen, dass Ihre App viele behandelte Ausnahmen enthält, ist das möglicherweise keine Option für Sie. An diesem Punkt ist Ihre einzige Möglichkeit, Ihre erste Anforderung zu erfüllen, entweder den Code zu schreiben, um ihn schließlich für Ausnahmeprotokollierungszwecke zu verwenden, oder Sie können in die direkte IL-Emissionsroute schauen, wie Greg Beech vorschlägt.

Ob der finally-Code ausgeführt wird, hängt jedoch vom Debugger ab, den Sie verwenden. Insbesondere bricht VS eine nicht abgeholte Exception, bevor die finally ausgeführt wird, und lässt Sie nicht fortfahren. Wenn Sie sich zu diesem Zeitpunkt nicht vom Prozess trennen, wird Ihr Protokollierungscode niemals ausgeführt. Mit anderen Worten, die zweite Anforderung wird die Erfüllung der ersten Anforderung beeinträchtigen.

    
Franci Penov 10.02.2009 21:37
quelle
2

Sie könnten Ihre Logik in eine benutzerdefinierte Klasse kapseln, etwa wie folgt:

%Vor%

Und benutze es als:

%Vor%

Hoffe, das hilft.

    
Jaime Febres 11.02.2009 00:37
quelle
1

Ist das nicht das Gleiche wie:

%Vor%

?

    
Dan Vinton 10.02.2009 20:13
quelle
1

Sie könnten eine kleine Assembly in VB.net schreiben oder für jemanden schreiben lassen, die eine TryFaultCatchFinally (of T) -Methode implementiert, die vier Delegaten akzeptiert:

  1. TryMethod - Eine Aktion (von T), um den "Try" -Block auszuführen.
  2. FaultMethod - Ein Prädikat (von T, Exception), das, wenn eine Ausnahme auftritt, aufgerufen wird, bevor irgendwelche "finally" Blöcke ausgeführt werden; Wenn es wahr zurückgibt, wird der Catch-Block ausgeführt - andernfalls wird es nicht ausgeführt.
  3. CatchMethod - Eine Aktion (Of T, Exception), die ausgeführt werden soll, wenn eine Ausnahme aufgetreten ist und FaultMethod true zurückgibt; passiert nach dem "finally" blocks run.
  4. FinallyMethod - Eine Aktion (OF, Exception, Boolean), die als "Finally" -Block ausgeführt wird. Die übergebene Ausnahme ist null, wenn TryMethod vollständig ausgeführt wurde, oder sie enthält die Ausnahme, durch die sie beendet wurde. Der Boolesche Wert ist wahr, wenn die Ausnahme abgefangen wurde, andernfalls falsch.

Beachten Sie, dass bei der Ausführung von FaultMethod möglicherweise der Status von Objekten, die die Ausnahme verursacht haben, untersucht werden kann, bevor ein solcher Zustand durch Finally-Blöcke zerstört wird. Man muss dabei Vorsicht walten lassen (alle Sperren, die beim Auslösen der Ausnahme gehalten wurden, werden weiterhin gehalten), aber die Fähigkeit mag manchmal immer noch nützlich sein, besonders beim Debugging.

Ich würde vorschlagen, dass die Routine so aussieht:

%Vor%     
supercat 06.01.2011 23:06
quelle
0

Nein, ich denke, das ist eine übliche Sprache, so wie Sie sie haben.

BEARBEITEN Um es klar zu sagen, die Strategien "catch" und "rethrow" bieten dieselbe Laufzeit-Semantik, ändern jedoch die Erfahrung, wenn der VS-Debugger angeschlossen ist. Tooling und Wartung ist wichtig; Debugging erfordert oft, dass Sie alle First-Exception-Exceptions "abfangen" und wenn Sie mit vielen "falschen" First-Chance-Exceptions aufgrund von catch-then-retrow in Ihrem Code enden, schadet es wirklich der Fähigkeit, den Code zu debuggen. In diesem Idiom geht es darum, gut mit dem Werkzeug zu interagieren und die Absicht klar auszudrücken (Sie wollen nicht "fangen", entscheiden nicht handhaben und neu starten, sondern nur protokollieren, dass eine Ausnahme passiert ist, aber lassen es geht vorbei).

    
Brian 10.02.2009 20:12
quelle
0

Haben Sie überlegt, das DebuggerStepThrough-Attribut zu verwenden? Ссылка

%Vor%     
Ron Jacobs 13.01.2011 15:00
quelle
-1
%Vor%     
Andy Mikula 10.02.2009 20:13
quelle

Tags und Links