捕获的.NET异常意外地为空

时间:2011-04-13 14:27:36

标签: c# exception-handling mef code-contracts

请参阅下文,了解正在进行的操作

我有一个非常奇怪的问题,捕获的异常是null。

代码使用MEF并努力报告组合错误。使用调试器我可以看到抛出异常(InvalidOperationException)但是当它被下面代码中的最后一个catch块捕获时,ex变量为null。在调试器和正常执行代码时都是如此。

static T ResolveWithErrorHandling<T>() where T : class
{
    try
    {
        IocContainer.Compose(Settings.Default.IocConfiguration);
        return IocContainer.Resolve<T>();
    }
    catch (ReflectionTypeLoadException ex)
    {
        // ... special error reporting for ReflectionTypeLoadException
    }
    catch (Exception ex)
    {
        // ex is null - that should not be possible!
        // ... general error reporting for other exception types
    }
    return null;
}

我用注释替换的代码是格式化错误消息的非常简单的代码。那里没什么奇怪的。

我试图改变代码以发现可能产生的影响:

  • 如果我删除第一个catch块(ReflectionTypeLoadException),则最终catch块中捕获的异常不再为null。
  • 如果我在第一个catch块中捕获另一个异常类型,则最终catch块中捕获的异常不再为null。
  • 如果我为InvalidOperationException添加一个catch块作为第一个catch块,则该块中捕获的异常不为null。
  • 如果我在两个catch块之间为InvalidOperationException添加一个catch块,则该块中捕获的异常为null。

项目使用Code Contracts并对编译器生成的代码进行后处理以检查合同。不幸的是,在没有对项目进行大手术的情况下,我没有想出办法摆脱测试目的。

我目前的解决方法是不捕获ReflectionTypeLoadException,而是在常规异常处理程序中分支ex的类型。

这种“不可能”行为的解释是什么?什么是ReflectionTypeLoadException catch block?


令人尴尬的是,异常不为空,并且根据C#标准15.9.5不能为空。

但是,在项目can mess up the display of local variables in the debugger中使用代码约定,因为编译器生成的IL代码可以通过代码约定重写,因此最终的IL与调试信息稍微不同步。在我的例子中,ex变量显示为null,即使它不是。在应用程序终止之前发生的错误报告的不幸性质意味着我认为由于ex为空而ex.MessageNullReferenceException内部抛出ex而导致错误报告未被调用抓住了。使用调试器,我能够“验证”ReflectionTypeLoadException为空,除非它实际上不为空。

{{1}}的catch块似乎影响调试器显示问题,这使我的困惑更加复杂。

感谢所有回复的人。

6 个答案:

答案 0 :(得分:12)

刚遇到同样的问题。我终于发现我用同样的名字捕获了不同的异常,就像你做的那样:

catch (ReflectionTypeLoadException ex)
{
    // ... 
}
catch (Exception ex)
{
    // ex is not null!
    // ...
}

两者都被命名为'ex'。更改其中一个名称可以解决这个问题,例如:

catch (ReflectionTypeLoadException reflectionEx)
{
    // ... 
}
catch (Exception ex)
{
    // ex is null - that should not be possible!
    // ...
}

答案 1 :(得分:5)

我遇到了同样的问题。在我的情况下,重命名异常变量(例如ex =&gt; ex1)允许我捕获任何异常......

答案 2 :(得分:4)

您应该检查IocContainer是否在某个时刻捕获Exception ex抛出ex.InnerException而不检查它是否为空。

C#愉快地接受throw null,最终在catch (Exception)

答案 3 :(得分:4)

我遇到了同样的问题。在调试器中查看时异常为null,即使正在捕获正确类型的异常 - UpdateException。我可以通过打开Exception Assistant来查看异常。

一旦我关闭“执行运行时合同检查”,就会捕获不再为空的异常。我一直在积极地使用代码合同进行一年,并且在我最近在这个特定项目中开始使用EF 4.1之前没有看到这个问题 - 但我不知道EF是否是关于捕获异常的控制变量是null

答案 4 :(得分:1)

异常实际上不是null,这是调试器的问题。 代码契约(ccrewrite)会更改IL操作码并扰乱调试器,因为leave.s操作码会转换为leave操作码。 这两个操作码具有不同的大小和指令地址更改,这就是当异常名称相同时调试器丢失的原因。

您可以在调试器中使用$ exception来解决此问题。

答案 5 :(得分:0)

我也有同样的情况。它恰好是Eclipse调试器的一个bug。 (实际上,这种情况只能是某些调试器错误的结果。)

Eclipse重启就足够了 - 运行时异常变得正常,而不是null。其他调试器可能不那么友好。