Blok catch nie jest oceniany, gdy wyjątki są wyrzucane z finallys
To pytanie pojawiło się, ponieważ kod, który wcześniej działał w .NET 4.0, nie powiódł się z nieobsługiwanym wyjątkiem w .NET 4.5, częściowo z powodu try / finallys. Jeśli chcesz poznać szczegóły, przeczytaj więcej na stronieMicrosoft connect. Użyłem go jako bazy dla tego przykładu, więc może być pomocne odniesienie.
KodDla osób, które zdecydowały się nie czytać o szczegółach stojących za tym pytaniem, oto bardzo szybkie spojrzenie na warunki, w których to się stało:
using(var ms = new MemoryStream(encryptedData))
using(var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
using(var sr = new StreamReader(cryptoStream))
Problem polega na tym, że istnieją wyjątki zDispose
metoda CryptoStream (ponieważ znajdują się wewnątrz instrukcji using, wyjątki te są wyrzucane z dwóch różnych bloków). GdycryptoStream.Dispose()
jest nazywany przezStreamReader
, theCryptographicException
Jest rzucony. Drugi razcryptoStream.Dispose()
nazywa się, w swojej instrukcji użycia, że rzuca aArgumentNullException
Poniższy kod usuwa większość niepotrzebnego kodu z linku podanego powyżej i rozwija wyciągi w try / finallys, aby wyraźnie pokazać, że są one wrzucane w bloki.
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");
}
}}
Uwaga: Try / Wreszcie odpowiada rozwiniętym za pomocą instrukcji z kodu, do którego istnieje odwołanie.
Wydajność:
Propagate, my children Throwing CryptographicExecption Throwing ArgumentException Caught ArgumentException Press any key to continue . . .
Nie wydaje się, żebyCryptographicException
blok catch jest zawsze wykonywany. Usunięcie tego bloku catch powoduje jednak, że wyjątek kończy działanie środowiska wykonawczego.
EDYCJA: Został zaktualizowany do najnowszej wersji specyfikacji. Ten, który akurat złapałem z MSDN, miał starsze brzmienie.Lost
został zaktualizowany doterminated
.
Nurkując w specyfikacji C #, sekcje 8.9.5 i 8.10 omawiają zachowanie wyjątkowe:
Gdy zostanie zgłoszony wyjątek, w tym wewnątrz bloku finally, formant jest przenoszony do pierwszej klauzuli catch w otaczającej instrukcji try. Powoduje to kontynuację instrukcji try, aż do znalezienia odpowiedniego.Jeśli wyjątek zostanie zgłoszony podczas wykonywania bloku finally, a wyjątek był już propagowany,ten wyjątek został zakończony„Zakończone” sprawia, że wydaje się, że pierwszy wyjątek będzie na zawsze ukryty przez drugi wyrzucony wyjątek, choć nie wydaje się, by tak się działo.
Jestem pewien, że gdzieś tu jest pytanieW większości przypadków łatwo jest zwizualizować to, co robi środowisko wykonawcze. Kod wykonuje się do pierwszego bloku w końcu (F1
) gdzie wyjątek jest rzucany. W miarę rozprzestrzeniania się wyjątku drugi wyjątek jest generowany z drugiego bloku wreszcie (F2
).
Zgodnie ze specyfikacjąCryptographicException
wyrzucony zF1
jest teraz zakończone, a środowisko wykonawcze szuka programu obsługi dlaArgumentException
. Środowisko wykonawcze znajduje procedurę obsługi i wykonuje kod w bloku catch dlaArgumentException
(C1
).
Tutaj jest mgła: specyfikacja mówi, że pierwszy wyjątek zostanie zakończony. Jeśli jednak drugi blok catch (C2
) jest usuwany z kodu,CryptographicException
który został rzekomo utracony, jest teraz nieobsługiwanym wyjątkiem, który kończy program. ZC2
obecny kod nie zakończy się z nieobsługiwanego wyjątku, więc na powierzchnipojawia się obsługiwać wyjątek, ale kod obsługi wyjątku w bloku nigdy nie jest wykonywany.
Pytania są zasadniczo takie same, ale sformułowane na nowo dla specyficzności.
Jak to jest, żeCryptographicException
zostaje rozwiązany z powoduArgumentException
wyjątek wyrzucony z zamykającego bloku finally, jako usunięciecatch (CryptographicException)
blok powoduje, że wyjątek nie jest obsługiwany i kończy działanie?
Ponieważ środowisko wykonawcze wydaje się obsługiwaćCryptographicException
kiedycatch (CryptographicException)
jest obecny, dlaczego kod wewnątrz bloku nie jest wykonywany?
Nadal przyglądam się faktycznemu zachowaniu tego i wiele odpowiedzi było szczególnie pomocnych w przynajmniej odpowiedzi na części powyższych pytań.
Kolejne ciekawe zachowanie, które ma miejsce, gdy uruchamiasz kod za pomocącatch (CryptographicException)
skomentowany blok, to różnica między .NET 4.5 a .NET 3.5. .NET 4.5 rzuciCryptographicException
i zakończ aplikację. .NET 3.5 wydaje się jednak zachowywać bardziej zgodnie ze specyfikacją C #, gdzie wyjątek.
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
W .NET 3.5 widzę to, co przeczytałem w specyfikacji. Wyjątek staje się „zagubiony” lub „zakończony”, ponieważ jedyną rzeczą, która musi zostać złapana, jestArgumentException
. Z tego powodu program może kontynuować wykonywanie. Mam tylko .NET 4.5 na moim komputerze, zastanawiam się, czy tak się dzieje w .NET 4.0?