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 CodeHier 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.
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 irgendwoZum 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.
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?
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.