【问题标题】:.NET exception caught is unexpectedly null捕获的 .NET 异常意外地为空
【发布时间】:2011-08-04 18:50:35
【问题描述】:

请参阅下文了解正在发生的事情

我有一个非常奇怪的问题,即捕获的异常为空。

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

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;
}

我用 cmets 替换的代码是格式化错误消息的非常简单的代码。没有什么奇怪的事情发生。

我已尝试更改代码以发现可能产生的影响:

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

项目使用Code Contracts,编译器生成的代码经过后处理以检查合约。不幸的是,为了测试目的,我还没有想出一种方法来摆脱这种情况,而无需对项目进行大手术。

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

对这种“不可能”的行为有何解释? ReflectionTypeLoadException 捕获块是怎么回事?


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

但是,在项目can mess up the display of local variables in the debugger 中使用代码契约,因为编译器生成的 IL 代码可以被代码契约重写,因此最终的 IL 与调试信息略有不同步。在我的情况下,ex 变量即使不是也显示为 null。在应用程序终止之前发生错误报告的不幸性质意味着我相信错误报告不会被调用,因为ex 为空并且ex.Message 在我的catch 块中抛出NullReferenceException。使用调试器,我能够“验证”ex 为空,但它实际上不为空。

ReflectionTypeLoadException 的 catch 块似乎会影响调试器显示问题,这使我更加困惑。

感谢所有回复的人。

【问题讨论】:

  • 真的有ReflectionTypeLoadException被扔到任何地方吗?
  • 只是一个注释,但您能否处理不同类型的异常,或者您是否只报告每种异常?如果这是通用报告并且您无法处理异常,那么我不确定您是否需要确定捕获范围。
  • 当您为 InvalidOperationExecption 添加捕获时会发生什么?
  • @Jaymz:是的,如果组合中包含的程序集无法加载。异常包含一些额外的信息,可以帮助解决问题,这些信息在 catch 块中提取。
  • 看起来catch (ReflectionTypeLoadException ex) 行吃掉了异常......

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


【解决方案1】:

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

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

两者都被命名为“前”。更改两个名称中的一个为我解决了这个问题,例如:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      C# 愉快地接受throw null,并以catch (Exception) 结束。

      【讨论】:

      • 如果你抛出一个空异常(例如,如果 ex.InnerException 为空)你会得到一个 NullReferenceException 抛出。在 C# 中,捕获的异常永远不能为空。除了在我的情况下......
      • 啊,我的错,我只是有一个预感,设置了一个测试用例,将鼠标悬停在 ex 变量上,看到“引用未设置为实例”,误认为它是一个真正的空值。
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        这个异常其实不是null,是调试器的问题。 代码契约 (ccrewrite) 更改 IL 操作码并扰乱调试器,因为 leave.s 操作码被转换为离开操作码。 这两个操作码的大小不同,指令地址发生变化,这就是为什么当异常名称相同时调试器会丢失的原因。

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

        【讨论】:

          【解决方案6】:

          我也遇到了同样的情况。它恰好是 Eclipse 调试器的一个错误。 (真的,这种情况只能是某些调试器的bug造成的。)

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-11-24
            • 1970-01-01
            • 2012-02-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多