Блок 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 приводит к тому, что исключение завершает среду выполнения.
РЕДАКТИРОВАТЬ: Это было обновлено до последней редакции спецификации. Тот, который я случайно получил от 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 для обработки исключения, но фактически код обработки исключения в блоке никогда не выполняется.
Вопросы в основном те же, но переформулированы для уточнения.
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?
Я все еще изучаю реальное поведение этого, и многие из ответов были особенно полезны, по крайней мере, для ответов на части вышеупомянутых вопросов.
Другое любопытное поведение, которое происходит, когда вы запускаете код с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?