这个问题的出现是因为以前在 .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()
被调用,在它的 using 语句中,它抛出一个ArgumentNullException
以下代码从上面提供的链接中删除了大部分不必要的代码,并将 using 语句展开为 try/finallys 以清楚地表明它们被抛出到 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 语句。
输出:
传播,我的孩子们 抛出 CryptographicExecption 抛出参数异常 捕获的参数异常 按任意键继续 。. .
似乎CryptographicException
从未执行过 catch 块。但是,删除该 catch 块会导致异常终止运行时。
更多信息
编辑:这已更新为规范的最新版本。我碰巧从 MSDN 上抢到的那个有更旧的措辞。 Lost
已更新为terminated
.
深入了解 C# 规范,第 8.9.5 和 8.10 节讨论异常行为:
- 当抛出异常时,包括从 finally 块内部,控制权被转移到封闭的 try 语句中的第一个 catch 子句。这将继续尝试语句,直到找到合适的语句。
- 如果在执行 finally 块期间抛出异常,并且已经传播了异常,则终止该异常
“终止”使第一个异常似乎永远被第二个抛出的异常隐藏,尽管它似乎不是正在发生的事情。
我确定问题就在这里
在大多数情况下,很容易可视化运行时在做什么。代码执行到F1
抛出异常的第一个 finally 块 ( )。随着异常的传播,第二个异常从第二个 finally 块 ( F2
) 中抛出。
根据规范,CryptographicException
抛出的 fromF1
现在已终止,运行时正在寻找ArgumentException
. ArgumentException
运行时找到一个处理程序,并为( )执行 catch 块中的代码C1
。
这是它变得模糊的地方:规范说第一个异常将被终止。C2
但是,如果从代码中删除CryptographicException
第二个 catch 块(就C2
目前而言,代码不会因未处理的异常而终止,因此表面上它似乎正在处理异常,但实际上块中的异常处理代码从未执行过。
问题
问题基本相同,但为了具体而重新措辞。
CryptographicException
由于ArgumentException
封闭的 finally 块引发的异常而终止,因为删除该catch (CryptographicException)
块会导致异常未处理并终止运行时,它是如何终止的?CryptographicException
由于运行时似乎正在处理catch (CryptographicException)
块存在的时间,为什么块内的代码没有执行?
额外信息 编辑
我仍在研究此问题的实际行为,并且许多答案对于至少回答上述问题的部分内容特别有帮助。
catch (CryptographicException)
另一个奇怪的行为是 .NET 4.5 和 .NET 3.5 之间的差异,当您运行带有注释的代码块时会发生这种情况。.NET 4.5 将抛出CryptographicException
并终止应用程序。但是,.NET 3.5 的行为似乎更符合异常所在的 C# 规范。
传播,我的孩子们 抛出 CryptographicExecption 未处理的异常:System.Security.Cryptography.CryptographicException [...] ram.cs:第 23 行 抛出参数异常 捕获的参数异常 走出了异常雷区
在 .NET 3.5 中,我看到了我在规范中读到的内容。异常变为“丢失”或“终止”,因为唯一需要捕获的是ArgumentException
. 因此,程序可以继续执行。我的机器上只有 .NET 4.5,我想知道这是否发生在 .NET 4.0 中?