除了基类数据(如堆栈跟踪)之外,NullReferenceException背后的设计决策是什么,不包含任何运行时特定信息?是否有Visual Studio的扩展,可以立即告诉您表达式的哪一部分为空?
答案 0 :(得分:68)
NRE是一个非常低级别的例外。当处理器要求从64K以下的地址读取数据时,处理器会生成硬件异常(“陷阱”)。虚拟内存空间的该区域始终未映射,特别是捕获指针错误。它以AccessViolation开始,当地址小于0x00010000时由CLR变为NRE。此时,异常的上下文非常少,所有已知的是导致陷阱的机器代码指令的地址。
无法将机器代码指令地址反向设计回程序中的命名变量。重要的是它以这种方式工作,否则抖动必须生成非常低效的代码。可以合理地做的就是恢复源代码行号。这需要包含行号信息的调试信息(.pdb)。 CLR知道如何读取.pdb文件并使用它来生成异常的堆栈跟踪。但是,由于JIT优化器执行的优化,它通常仍然不准确,它会移动代码。您只能获得Debug构建的保证匹配。 Release版本的PDB不包含源行号信息的原因。你可以改变它。
这个问题有一个非常简单的解决方案。在让运行时执行此操作之前,请自行检查null并生成您自己的异常。测试相当便宜,远不到一纳秒。
答案 1 :(得分:23)
您可以设置Visual Studio立即中断Throw for NullReferenceException
,而不是首先在catch块中。
然后你会在导致NullReferenceException
的行中断。
答案 2 :(得分:5)
在运行时确定什么是空的并没有什么简单的方法 - 必须从IL中反编译和推断出必要的信息,这是足够低的水平,所有它知道的是“顶部的项目” stack是null“但不是那个项目是如何到达那里的。
(我不知道这样的扩展,但它会是一个很好的调试器增强功能)
答案 3 :(得分:4)
NullReferenceException
由运行时引发,而不是由语言引发。运行时不知道引用的来源;它只知道一条指令试图使用空引用。
实际上,异常“可能”是本机窗口“无效访问冲突”SEH异常的结果,它被运行时捕获并转换为IL异常(“可能”:我没有检查这是否是但是,我猜测JIT代码表现最好的方式是什么。
答案 4 :(得分:3)
并非所有有关您的代码的信息都在运行时可用。例如,局部变量名称不可用。因此,不可能像“局部变量myObj为null,但它的成员已被调用”这样的消息抛出异常。此外,在运行时有很多非用户编写的对象(例如,为闭包/匿名类型/迭代器等生成的类),它们也可能是null-ref异常的来源。
答案 5 :(得分:0)
如果您有调试符号,则可以追踪导致异常的行。如果它足够简单,您将直接获得null
值。否则(考虑a.Value = b.Do(c.GetX(),d.GetY(z.ToString()));
)您必须使用IDE进行调试