抛出NullReferenceException时检测目标对象是什么

时间:2008-09-22 15:35:37

标签: .net exception

我确信我们在某个时间或某个时间都收到了非常模糊的“对象引用未设置为对象实例”异常。识别出问题的对象通常是设置断点和检查每个语句中的所有成员的繁琐任务。

有没有人有任何技巧可以通过编程方式或其他方式轻松有效地识别导致异常的对象?

- 修改

看起来我很模糊,就像异常=)。关键是_不必调试应用程序以找到错误的对象。编译器/运行时确实知道该对象已被分配/声明,并且该对象尚未被实例化。有没有办法在捕获的异常中提取/识别这些细节

@ W. Craig Trader

您的解释是设计问题的结果可能是我能得到的最佳答案。我对防御性编码非常强迫,并且在经过一段时间修复我的习惯之后设法摆脱了大部分这些错误。其余的只是 调整 我没有尽头,并引导我将这个问题发布到社区。

感谢大家的建议。

10 个答案:

答案 0 :(得分:17)

在抛出NRE的位置,没有目标对象 - 这是异常的点。您最希望的是捕获发生异常的文件和行号。如果您在识别导致问题的对象引用时遇到问题,那么您可能需要重新考虑编码标准,因为这听起来像是在一行代码上做得太多。

通过内置语言结构或通过库,Design by Contract可以更好地解决此类问题。 DbC建议预先检查任何传入的参数,以获取超出范围数据的方法(即:Null)并抛出异常,因为该方法不适用于错误的数据。

[编辑以匹配问题编辑:]

我认为NRE描述会误导你。 CLR所具有的问题是,当对象引用为Null时,它被要求取消引用对象引用。以这个示例程序为例:

public class NullPointerExample {
  public static void Main()
  {
    Object foo;
    System.Console.WriteLine( foo.ToString() );
  }
}

当你运行它时,它会在第5行抛出一个NRE,当它试图评估foo上的ToString()方法时。没有要调试的对象,只有未初始化的对象引用(foo)。有一个类和一个方法,但没有对象。


回复:Chris Marasti-Georg的answer

你永远不应该自己抛出NRE - 这是具有特定含义的系统异常:CLR(或JVM)试图评估未初始化的对象引用。如果您预先检查对象引用,则抛出某种无效的参数异常或特定于应用程序的异常,但不抛出NRE,因为您只会混淆下一个必须维护您的应用程序的程序员。

答案 1 :(得分:17)

正如一些答案所指出的,告诉Visual Studio在Throw上打破NullReferenceException。

如何在抛出未处理的异常时告诉VS中断

  • 调试菜单|例外(或 Ctrl + Alt + E
  • 深入研究公共语言运行时异常
  • 钻入系统
  • 查找System.NullRefernceException,并在抛出此异常时选中Break框,而不是允许它继续执行任何Catch块到位

所以现在当它发生时,VS将立即中断,并且Current Statement行将位于评估为null的表达式上。

此工具对各种异常都很有用,包括自定义异常(可以添加完全限定的类型名称,VS会在调试时匹配)

这种方法的一个缺点是,如果在调试器中加载了代码,这些代码遵循了抛出和捕获大量正在寻找的异常的错误做法,在这种情况下它会变回干草堆/针头问题(除非你能解决这个问题 - 当然你已经解决了两个问题:)


另一个可能派上用场的技巧(但仅限于某些语言)是使用When(或等效)关键字...在VB中,这看起来像

Try
  ' // Do some work           '
Catch ex As Exception When CallMethodToInspectException(ex)

End Try

这里的技巧是在将callstack展开到Catch块之前评估When表达式。因此,如果您正在使用调试器,则可以设置该表达式的断点,如果查看callstack窗口(Debug | Windows | Callstack),您可以查看并导航到触发异常的行。

(您可以选择从CallMethodToInspectException返回false,因此将忽略Catch块,运行时将继续通过堆栈搜索适当的Catch块 - 这可以允许不影响行为的日志记录,以及比捕获和重新投掷更少的开销)


如果您只对非交互式日志记录感兴趣,那么假设您已经获得了Debug版本(或者在某种程度上,因为您已经处理了优化问题,使用PDB发布版本),您可以获得所需的大部分信息使用包含的stack-trace-with-line-number来跟踪Exception ToString中的错误。

如果行号不够,你也可以通过提取异常的StackTrace(使用上述技术,或者只是在中)来获取列号(因此非常多,特定的本地或表达式为null) catch块本身):

int colNumber = new System.Diagnostics.StackTrace(ex, true).GetFrame(0).GetFileColumnNumber();

虽然我没有看到它对NullReference或其他运行时生成的异常的作用,但也可能有兴趣将Exception Hunter视为静态分析工具。

答案 2 :(得分:1)

除了查看堆栈跟踪之外,你真的没什么可做的;如果您在同一行代码中取消引用多个对象引用,则无法在不设置断点的情况下确定哪一个为null。你可以通过每行只取消引用一个对象来避免这种情况,但这会导致一些非常糟糕的代码。

答案 3 :(得分:1)

好吧,你无法真正识别出它不存在的对象,因此你得到的例外。

答案 4 :(得分:1)

行#和文件通常是查找罪魁祸首所需的全部内容。如果您是抛出异常的人,请考虑使用ArgumentNullException(如果适用),或检查空值并抛出具有关于空字段的更多详细信息的NullReferenceException

编辑@您的编辑:)

AFAIK,您必须检查堆栈跟踪字符串以获取该行#和文件。最好的办法是获得最内层的异常,然后查看其堆栈跟踪的第一行。如果您希望能够以编程方式解析该信息以找出导致该null的字段,并使用该字段的名称执行某些操作,我担心您将失去运气。

@W。克雷格·特拉德

好点。对于传递给方法的null值,应抛出ArgumentNullException。对于尚未初始化的成员变量,像InvalidStateException这样的东西可能会很好。不幸的是,我在MSDN中找不到任何此类异常。滚动自己?

答案 5 :(得分:0)

答案 6 :(得分:0)

如果您正在捕获友好用户消息或日志记录的异常,您可能希望调试器在调试时停止异常。转到Debug / Exceptions并检查您希望调试器停止运行的异常类型,在您的情况下是System.NullReferenceException。

答案 7 :(得分:0)

将VS设置为中断异常,然后当您收到错误时,它通常很明显是什么行。堆栈跟踪窗口将告诉您如何到达那里。除此之外别无他法。

答案 8 :(得分:0)

供参考,类似的主题:Should I catch exceptions only to log them?

突出点是您希望有效捕获异常。根据我的经验,目标是确保程序员检查代码中的空引用 - 但是我们知道实际上,我们错过了一些。 UI代码应该有一定程度的异常处理。我喜欢我对这个问题的回答:My Answer。更重要的是,1800 information的评论指出你只是抛出,而不是抛出ex来捕获整个堆栈跟踪,这是你最终调试这些问题的方法。

答案 9 :(得分:0)

关于设置Visual Studio以捕获异常(如建议的here),一旦解决了问题,请不要忘记删除此选项。我只是浪费了半个小时试图弄清楚为什么我的应用程序在System.Windows.Forms的某些部分深处悬挂....