Этот вопрос возник из-за того, что код, который работал ранее в .NET 4.0, не удался с необработанным исключением в .NET 4.5, частично из-за try/finallys. Если вы хотите получить подробную информацию, прочитайте больше на Microsoft connect. Я использовал его в качестве основы для этого примера, поэтому было бы полезно ссылаться.
Код
Для людей, которые решили не читать подробностей, стоящих за этим вопросом, здесь очень быстрый взгляд на условия, в которых это произошло:
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, чтобы четко показать, что они в конечном итоге блокируются.
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/окончательно соответствует расширению с помощью операторов из ссылочного кода.
Вывод:
Propagate, my children Throwing CryptographicExecption Throwing ArgumentException Caught ArgumentException Press any key to continue . . .
Кажется, что блок catch CryptographicException
не выполняется. Однако удаление этого блока catch приводит к тому, что исключение завершает среду выполнения.
Немного больше информации
EDIT: обновление было обновлено до последней версии спецификации. Тот, с которым я столкнулся с MSDN, имел более старые формулировки. Lost
обновлен до terminated
.
Дайвинг в спецификации С#, разделы 8.9.5 и 8.10 обсуждают поведение исключений:
- Когда генерируется исключение, в том числе из блока finally, управление передается в первое предложение catch в прилагаемой инструкции try. Это продолжается до тех пор, пока не будет найдено подходящее.
- Если во время выполнения блока finally возникает исключение, и уже было распространено исключение, это исключение завершается
"Terminated" заставляет казаться, что первое исключение навсегда будет скрыто вторым исключенным броском, хотя, похоже, это не то, что происходит.
Я уверен, что вопрос здесь где-то
По большей части легко визуализировать, что делает среда выполнения. Код выполняет первый блок finally (F1
), где генерируется исключение. По мере распространения исключения второе исключение выбрасывается из второго блока finally (F2
).
Согласно спецификации, CryptographicException
, выкинутый из F1
, теперь завершен, а среда выполнения ищет обработчик для ArgumentException
. Среда выполнения находит обработчик и выполняет код в блоке catch для ArgumentException
(C1
).
Вот где он становится туманным: спецификация говорит, что первое исключение будет прекращено. Однако, если второй блок catch (C2
) удален из кода, CryptographicException
, который предположительно был потерян, теперь является необработанным исключением, которое завершает работу программы. При наличии C2
код не будет завершен из необработанного исключения, поэтому на поверхности он, похоже, обрабатывает исключение, но фактически код обработки исключений в блоке никогда не выполняется.
Вопросы
Вопросы в основном одинаковы, но переформулированы для специфики.
-
Как получается, что
CryptographicException
завершается из-за исключенияArgumentException
, выведенного из закрывающего блока finally, поскольку удаление блокаcatch (CryptographicException)
приводит к тому, что исключение не обрабатывается и завершается среда выполнения? -
Поскольку время выполнения, по-видимому, обрабатывает
CryptographicException
, когда присутствует блокcatch (CryptographicException)
, почему код внутри блока не выполняется?
Дополнительная информация Редактировать
Я все еще изучаю фактическое поведение этого, и многие из ответов были особенно полезны, по крайней мере, для ответа на некоторые из вышеуказанных вопросов.
Другое любопытное поведение, которое случается при запуске кода с блоком catch (CryptographicException)
, закомментировано, - это разница между .NET 4.5 и .NET 3.5..NET 4.5 выбрасывает CryptographicException
и завершает работу приложения..NET 3.5, однако, по-видимому, ведет себя больше в соответствии с спецификацией С#, где исключение.
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?