Der Catch-Block wird nicht ausgewertet, wenn von finallys Ausnahmen ausgelöst werden

Diese Frage entstand, weil Code, der zuvor in .NET 4.0 funktionierte, mit einer nicht behandelten Ausnahme in .NET 4.5 fehlschlug, teilweise aufgrund von try / finallys. Weitere Informationen finden Sie unterMicrosoft verbinden. Ich habe es als Basis für dieses Beispiel verwendet, daher kann es hilfreich sein, darauf zu verweisen.

Der Code

Hier ein kurzer Überblick über die Umstände, unter denen dies geschehen ist:

using(var ms = new MemoryStream(encryptedData))
using(var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
using(var sr = new StreamReader(cryptoStream))

Dieses Problem ist, dass es Ausnahmen gibt, die von der geworfen werdenDispose Methode von CryptoStream (da sie sich in einer using-Anweisung befinden, werden diese Ausnahmen zufällig von zwei verschiedenen finally-Blöcken ausgelöst). WanncryptoStream.Dispose() wird von der angerufenStreamReader, dasCryptographicException ist geworfen. Das zweite MalcryptoStream.Dispose() heißt, wirft in seiner using-Anweisung aArgumentNullException

Mit dem folgenden Code wird der größte Teil des unnötigen Codes aus dem oben angegebenen Link entfernt, und die using-Anweisungen werden in try / finallys aufgelöst, um deutlich zu machen, dass sie in finally-Blöcke geworfen werden.

using System;
using System.Security.Cryptography;
namespace Sandbox
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                try
                {
                    try
                    {
                        Console.WriteLine("Propagate, my children");
                    }
                    finally
                    {
                        // F1
                        Console.WriteLine("Throwing CryptographicExecption");
                        throw new CryptographicException();
                    }
                }
                finally
                {
                    // F2
                    Console.WriteLine("Throwing ArgumentException");
                    throw new ArgumentException();
                }
            }
            catch (ArgumentException)
            {
                // C1
                Console.WriteLine("Caught ArgumentException");
            }
            // Same behavior if this was in an enclosing try/catch
            catch (CryptographicException)
            {
                // C2
                Console.WriteLine("Caught CryptographicException");
            }

            Console.WriteLine("Made it out of the exception minefield");
        }
    }}

Hinweis: Die try / finally-Entsprechung wird mithilfe von Anweisungen aus dem angegebenen Code erweitert.

Ausgabe:

    Propagate, my children
    Throwing CryptographicExecption
    Throwing ArgumentException
    Caught ArgumentException
    Press any key to continue . . .

Es scheint nicht, dass dieCryptographicException catch block wird immer ausgeführt. Durch das Entfernen dieses Catch-Blocks wird jedoch die Laufzeit durch die Ausnahme beendet.

Ein bisschen mehr Information

BEARBEITEN: Dies wurde auf die neueste Version der Spezifikation aktualisiert. Die, die ich zufällig von MSDN abgeholt habe, hatte einen älteren Wortlaut.Lost wurde aktualisiert aufterminated.

In den Abschnitten 8.9.5 und 8.10 der C # -Spezifikation wird das Ausnahmeverhalten erörtert:

Wenn eine Ausnahme ausgelöst wird, auch aus einem finally-Block, wird die Steuerung an die erste catch-Klausel in einer einschließenden try-Anweisung übertragen. Dies setzt try-Anweisungen fort, bis eine geeignete gefunden wurde.Wenn während der Ausführung eines finally-Blocks eine Ausnahme ausgelöst wird und bereits eine Ausnahme weitergegeben wurde,Diese Ausnahme wird beendet

"Abgebrochen" lässt den Eindruck entstehen, dass die erste Ausnahme für immer von der zweiten Ausnahmebedingung verborgen bleibt, obwohl dies nicht der Fall zu sein scheint.

Ich bin sicher, die Frage ist hier irgendwo

Zum größten Teil ist es einfach zu visualisieren, was die Laufzeit tut. Der Code wird bis zum ersten finally-Block ausgeführt (F1) wo eine Ausnahme geworfen wird. Während sich die Ausnahme ausbreitet, wird die zweite Ausnahme vom zweiten finally-Block geworfen (F2).

Nach der Spezifikation, dieCryptographicException geworfen vonF1 ist nun beendet und die Runtime sucht nach einem Handler für denArgumentException. Die Laufzeit findet einen Handler und führt den Code im catch-Block für den ausArgumentException (C1).

Hier wird es neblig: Die Spezifikation besagt, dass die erste Ausnahme beendet werden würde. Wenn jedoch der zweite Fangblock (C2) wird aus dem Code entfernt, derCryptographicException das war angeblich verloren, ist jetzt eine unbehandelte ausnahme die das programm beendet. Mit demC2 Derzeit wird der Code nicht von einer unbehandelten Ausnahme beendet, also oberflächlich betrachteterscheint um die Ausnahme zu behandeln, aber der eigentliche Code für die Ausnahmebehandlung im Block wird nie ausgeführt.

Fragen

Die Fragen sind im Grunde die gleichen, jedoch aus Gründen der Spezifität umformuliert.

Wie kommt es, dass dieCryptographicException wird aufgrund der gekündigtArgumentException Ausnahme, die vom umschließenden finally-Block geworfen wird, wie das Entfernen descatch (CryptographicException) Block bewirkt, dass die Ausnahme nicht behandelt wird und die Laufzeit beendet wird?

Da scheint die Laufzeit das zu handhabenCryptographicException wenn dercatch (CryptographicException) Block ist vorhanden. Warum wird der Code im Block nicht ausgeführt?

Zusätzliche informative Bearbeitung

Ich untersuche immer noch das tatsächliche Verhalten und viele der Antworten waren besonders hilfreich, um zumindest Teile der obigen Fragen zu beantworten.

Ein weiteres merkwürdiges Verhalten, das auftritt, wenn Sie den Code mit dem Befehl ausführencatch (CryptographicException) Block auskommentiert, ist der Unterschied zwischen .NET 4.5 und .NET 3.5. .NET 4.5 wirft dieCryptographicException und beenden Sie die Anwendung. .NET 3.5 scheint sich jedoch eher nach der C # -Spezifikation zu verhalten, bei der die Ausnahme besteht.

Propagate, my children
Throwing CryptographicExecption

Unhandled Exception: System.Security.Cryptography.CryptographicException [...]
ram.cs:line 23
Throwing ArgumentException
Caught ArgumentException
Made it out of the exception minefield

In .NET 3.5 sehe ich, was ich in der Spezifikation gelesen habe. Die Ausnahme wird "verloren" oder "beendet", da das einzige, was jemals gefangen werden muss, das istArgumentException. Dadurch kann das Programm weiter ausgeführt werden. Ich habe nur .NET 4.5 auf meinem Computer. Ich frage mich, ob dies in .NET 4.0 passiert.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage