Блок Catch не оценивается, когда выбрасываются исключения из finally

Этот вопрос возник из-за того, что код, который ранее работал в .NET 4.0, не работал с необработанным исключением в .NET 4.5, частично из-за try / finally. Если вам нужны подробности, читайте больше наMicrosoft Connect, Я использовал его в качестве основы для этого примера, так что это может быть полезно для ссылки.

The code

Для людей, которые решили не читать подробности этого вопроса, очень кратко рассмотрим условия, в которых это произошло:

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

Эта проблема заключается в том, что есть исключенияDispose метод CryptoStream (поскольку они находятся внутри оператора using, эти исключения генерируются из двух разных блоков finally). когдаcryptoStream.Dispose() называетсяStreamReader,CryptographicException брошен Второй разcryptoStream.Dispose() называется, в своем операторе использования он бросаетArgumentNullException

Следующий код удаляет большую часть ненужного кода из ссылки, приведенной выше, и раскручивает операторы using в try / finally, чтобы ясно показать, что они выбрасываются в блоки finally.

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");
        }
    }}

Примечание: try / finally соответствует расширенным операторам using из ссылочного кода.

Выход:

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

Похоже, чтоCryptographicException блок catch когда-либо выполняется. Однако удаление этого блока catch приводит к тому, что исключение завершает среду выполнения.

A little more information

РЕДАКТИРОВАТЬ: Это было обновлено до последней редакции спецификации. Тот, который я случайно получил от MSDN, имел более старую формулировку.Lost был обновлен доterminated.

Погружаясь в спецификации C #, разделы 8.9.5 и 8.10 обсуждают поведение исключений:

When an exception is thrown, including from inside a finally block, control is transferred to the first catch clause in an enclosing try statement. This continues up try statements until a suitable one is found. If an exception is thrown during execution of a finally block, and an exception was already being propagated, that exception is terminated

& Quot; Отменено & Quot; Создается впечатление, что первое исключение всегда будет скрыто вторым брошенным исключением, хотя, похоже, это не то, что происходит.

I'm sure the question is in here somewhere

По большей части легко визуализировать, что делает среда выполнения. Код выполняется до первого блока finally (F1) где исключение. Поскольку исключение распространяется, второе исключение выдается из второго блока finally (F2).

Согласно спецификации,CryptographicException брошенный изF1 теперь завершено, и среда выполнения ищет обработчик дляArgumentException, Среда выполнения находит обработчик и выполняет код в блоке catch дляArgumentException (C1).

Вот где это становится туманным: спецификация говорит, что первое исключение будет прекращено. Однако, если второй блок catch (C2) удаляется из кода,CryptographicException это было предположительно потеряно, теперь является необработанным исключением, которое завершает программу. СC2 В настоящее время код не будет завершен из необработанного исключения, поэтому на первый взглядappears для обработки исключения, но фактически код обработки исключения в блоке никогда не выполняется.

Questions

Вопросы в основном те же, но переформулированы для уточнения.

How is it that the CryptographicException becomes terminated due to the ArgumentException exception thrown from the enclosing finally block, as removing the catch (CryptographicException) block causes the exception to go unhandled and terminate the runtime?

Since the runtime seems to be handling the CryptographicException when the catch (CryptographicException) block is present, why is the code inside of the block not executing?

Extra informational Edit

Я все еще изучаю реальное поведение этого, и многие из ответов были особенно полезны, по крайней мере, для ответов на части вышеупомянутых вопросов.

Другое любопытное поведение, которое происходит, когда вы запускаете код сcatch (CryptographicException) Блок закомментирован, это разница между .NET 4.5 и .NET 3.5. .NET 4.5 скинетCryptographicException и прекратить приложение. Тем не менее, .NET 3.5 ведет себя в большей степени в соответствии со спецификацией C #, за исключением.

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

В .NET 3.5 я вижу то, что читаю в спецификации. Исключение становится «потерянным» или «прекращенным», поскольку единственное, что когда-либо нужно поймать, этоArgumentException, Из-за этого программа может продолжить выполнение. У меня только .NET 4.5 на моей машине, интересно, если это произойдет в .NET 4.0?

Ответы на вопрос(3)

Ваш ответ на вопрос