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.

Kod

Dla 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.

Trochę więcej informacji

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 pytanie

W 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

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?

Dodatkowe informacje Edytuj

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?

questionAnswers(3)

yourAnswerToTheQuestion