代码之家  ›  专栏  ›  技术社区  ›  Carlo

.NET中的空引用异常

  •  2
  • Carlo  · 技术社区  · 14 年前

    我们的应用程序有这个大问题。它是一个相当大的应用程序,有几个模块和成千上万行的代码。应用程序的许多部分设计为仅在引用另一个对象时才存在,例如,如果没有房子对象,个人对象就永远不可能存在,因此,如果在应用程序中的任何一点上说:

    bool check = App.Person.House == null;
    

    check 应该永远是 false (按设计),所以,为了继续使用这个示例,在创建模块、测试、调试时,app.person.house永远不会为空,但是一旦我们将应用程序发送到我们的客户机,他们就开始得到一堆 NullReferenceException 对于按设计,不应具有空引用的对象。他们告诉我们这个bug,我们试图在这里复制它,但是90%的时候我们不能复制,因为在这里它可以正常工作。

    这个应用程序是用c_和wpf开发的,根据设计,它只能在Windows XP SP 3和.NET Framework v3.5上运行,所以我们知道用户有和我们这里相同的操作系统、服务包和.NET Framework版本,但他们仍然会得到这种奇怪的结果。 NullReferenceExceptions 我们无法复制。

    所以,我只是想知道是否有人以前看到过这个,以及你是如何修复的,我们的应用程序在5台不同的计算机上每天至少运行8小时,而且我们从来没有看到过这些异常,这只是由于某种原因发生在客户机上。

    任何能使我们更接近解决这个问题的想法,任何线索,任何解决方案都将非常感谢。

    谢谢!

    5 回复  |  直到 10 年前
        1
  •  10
  •   Jon Skeet    14 年前

    嗯,你还没有告诉我们很多关于房子的事情…它是可写的吗?如果是这样,将验证放入setter,在 最少的 如果值为空,则记录日志-理想情况下抛出 ArgumentNullException 立即(我相信当你的数据被破坏时最好停止,而不是继续,希望生活会自己解决)。如果它只是设置为构造函数中的支持字段,请检查它,然后再次抛出 ArgumentNullException 如果它是空的。

    如果它是以某种方式计算的,那就更难了——此时,您可能应该在getter和log中进行测试(以某种方式,您的客户很容易将信息返回给您),尽可能多地测试您认为相关的信息。

    编辑:正如已经指出的,这可以应用于表达式中的任何级别-因此您很可能希望对每个级别应用相同类型的验证和日志记录。

        2
  •  6
  •   Henk Holterman    14 年前

    线

     bool check = App.Person.House == null;
    

    在以下任一情况下都将引发空引用异常 App App.Person 是空的。

    你说“设计不能为空”,但这是不可能从你的描述来证实的。尽管看起来很清楚,您的用户只是沿着一条路径通过您的程序,而这个路径在您的测试用例中没有涉及。

    如果你不能发现你的设计中的缺陷,那么实际的方法是通过内部检查来扩展你的应用程序。我会推荐一个好的追踪系统和(很多) System.Diagnostics.Trace.Assert(xx != null);

        3
  •  0
  •   Rune Grimstad    14 年前

    如何实例化对象?可以是Person对象为空吗?还是应用程序对象?这可以解释例外情况。

    如果您找不到问题,我建议您向应用程序添加日志记录,并检查实例化对象的代码和使用它的代码,以查看在创建对象之前是否以某种方式访问这些对象。

    另一个可能的问题可能是异常处理。确保您没有尝试捕获只接受异常的块。这些可能是一些真正头痛的根源。

    不管怎样。这些只是建议。如果不了解你的系统,很难做出更好的猜测。

        4
  •  0
  •   Charles Bretana    14 年前

    一个非常试探性的猜测是,您对YR的编码没有一致的异常处理模式,而其他一些异常被抛出并被不适当地吞没,这使得执行线程到达一个运行代码的点,该点假设对象已被实例化,而实际上先前吞没或忽略的异常操作阻止了它被实例化,并导致它仍然为空。

        5
  •  0
  •   BenMorel Manish Pradhan    10 年前

    在不理解代码的情况下,很难猜测为什么会发生这种情况。典型的来源是

    • 访问统一的值(保留为默认的空值),但C编译器通常会警告您
    • 强制转换(对象作为类型)当对象是意外类型时,通常在早期测试中很快就会找到这些类型,除非您有一个完全未测试的代码路径
    • 分配失败。在clr中,尽管它们引发了内存异常
    • 数据库读取。这些很可能是原始值的罪魁祸首,但对于复杂的类则不是。不过,ORM层可能会掩盖错误并简单地将空值分配给成员,从而触发稍后看到的错误。
    • 逻辑错误。另一个可能的罪魁祸首是,您的客户机使用该应用程序的方式使用了一个不同的代码路径,而该路径只是不分配值。

    当我遇到类似的问题时,我最终向应用程序添加了工具。主要是日志记录( log4net 在我的例子中)和异常报告。最初我使用了assert,但是clr断言在生产中是一个灾难,因为它们弹出了需要用户交互的大中断消息框,完全不适合服务和后台运行的应用程序。因此,我更喜欢捕获异常,从异常上下文创建一个报告,并将报告发送回母舰进行分析。最终,我能够从那些异常报告中找到所有问题。我最终围绕这个概念在 http://bugcollect.com 并将它与log4net集成,这样我就可以从应用程序的所有部署(从所有客户机)中实时获取报告。