【发布时间】:2012-08-22 11:21:17
【问题描述】:
这个问题的出现是因为以前在 .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))
这个问题是 CryptoStream 的 Dispose 方法抛出了异常(因为它们在 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 块的执行过程中抛出异常,并且异常已经被传播,该异常被终止
“终止”使第一个异常似乎永远被第二个抛出的异常隐藏,尽管它似乎不是正在发生的事情。
我确定问题就在这里
在大多数情况下,很容易可视化运行时在做什么。代码执行到抛出异常的第一个 finally 块 (F1)。随着异常的传播,第二个异常从第二个 finally 块 (F2) 中抛出。
根据规范,从F1 抛出的CryptographicException 现在已终止,运行时正在寻找ArgumentException 的处理程序。运行时找到一个处理程序,并为ArgumentException (C1) 执行catch 块中的代码。
这里是模糊不清的地方:规范说第一个异常将被终止。但是,如果从代码中删除第二个 catch 块 (C2),则应该丢失的 CryptographicException 现在是一个未处理的异常,会终止程序。在C2 存在的情况下,代码不会因未处理的异常而终止,因此表面上它似乎正在处理异常,但块中的实际异常处理代码永远不会执行。
问题
问题基本相同,但为了具体而重新措辞。
-
CryptographicException因封闭 finally 块引发的ArgumentException异常而终止,因为删除catch (CryptographicException)块会导致异常未处理并终止运行时? -
既然存在
catch (CryptographicException)块时运行时似乎正在处理CryptographicException,为什么块内的代码没有执行?
额外信息编辑
我仍在调查此问题的实际行为,其中许多答案对至少回答上述部分问题特别有帮助。
另一个奇怪的行为是 .NET 4.5 和 .NET 3.5 之间的差异,当您运行带有 catch (CryptographicException) 块注释的代码时会发生这种情况。 .NET 4.5 将抛出 CryptographicException 并终止应用程序。然而,.NET 3.5 的行为似乎更符合异常所在的 C# 规范。
在 .NET 3.5 中,我看到了我在规范中读到的内容。异常变为“丢失”或“终止”,因为唯一需要捕获的是ArgumentException。因此,程序可以继续执行。我的机器上只有 .NET 4.5,我想知道这是否发生在 .NET 4.0 中?
【问题讨论】:
-
您的代码准确地展示了语言规范告诉您的内容。如果你用“replaced”代替“lost”也许会有所帮助。
-
@HansPassant - 我在上面发布的精简代码以及连接问题链接中的实际代码都表明异常丢失了,但只有当我尝试捕获它时.当
CryptographicException的catch 块被删除时,该异常将未处理并终止应用程序,因此它永远不会丢失或替换。如果存在 catch 块,应用程序会成功,但代码不会执行。 -
finally 块在确定要继续的地方之前不会执行。如果找不到 catch 子句,那将无处可去。
-
@HansPassant - 我不确定我是否跟随。 finally 块执行,因为它们所做的只是抛出异常。加密 catch 块没有执行,我觉得这很令人困惑,因为运行时的行为就像 catch 的存在正在处理该异常。如果删除它,运行时会以未处理的异常终止,在处理
ArgumentNullException之后。我不确定我对自己的解释有多好。如果您有 .NET 4.5,我鼓励您测试我在上面发布的代码,甚至是顶部链接的连接问题中的代码,因为它会更好地解释它。 -
@samuelneff - 我希望没有人会这样做,但是当图书馆不遵循指导方针并在处理时抛出异常时,它发生的数量令人惊讶。 CryptoStream 是执行此操作的基类库的示例,也是此问题所基于的。
标签: c# .net specifications .net-4.5