关于这个.NET未处理异常信息:
对象引用未设置到对象的实例。
.NET为什么不显示哪个对象是null
呢?
我知道我可以检查null
并解决错误。然而,为什么.NET不帮忙指出哪个对象具有空引用,哪个表达式触发了NullReferenceException
?
关于这个.NET未处理异常信息:
对象引用未设置到对象的实例。
.NET为什么不显示哪个对象是null
呢?
我知道我可以检查null
并解决错误。然而,为什么.NET不帮忙指出哪个对象具有空引用,哪个表达式触发了NullReferenceException
?
(有关Visual Studio 2017中新的异常助手的信息,请参见本答案末尾)
考虑以下代码:
String s = null;
Console.WriteLine(s.Length);
在第二行代码中抛出了NullReferenceException
异常,你想知道为什么.NET没有告诉你是s
为null导致了异常。
要理解为什么你没有得到这个信息,你应该记住执行的不是C#源代码而是IL代码:
IL_0001: ldnull IL_0002: stloc.0 // s IL_0003: ldloc.0 // s IL_0004: callvirt System.String.get_Length IL_0009: call System.Console.WriteLine
正是callvirt
操作码引发了NullReferenceException
异常,并且当评估堆栈上的第一个参数是null引用时(使用ldloc.0
加载的参数)时会抛出该异常。
如果.NET能够确定是s
为null引用,那么它应该以某种方式跟踪该参数在评估堆栈上的位置。在这种情况下,我们很容易看出是s
为null引用,但是如果该值是从另一个函数调用的返回值并且未存储在任何变量中,则怎么办呢?无论如何,在像.NET虚拟机这样的虚拟机中,这种信息都不是您想跟踪的信息。
为了避免这个问题,我建议您在所有公共方法调用中执行参数空值检查(除非您允许null引用):
public void Foo(String s) {
if (s == null)
throw new ArgumentNullException("s");
Console.WriteLine(s.Length);
}
如果将 null 传递给该方法,您将获得一个异常,它准确地描述了问题所在(即 s 为 null)。
四年后,Visual Studio 2017 现在有了一个新的异常助手,尝试告诉您当抛出 NullReferenceException 时哪些是 null。即使是当方法的返回值为 null 时,它也能够提供所需的信息:
请注意,这仅适用于 DEBUG 构建。
NullReferenceException
的实现确实依赖于硬件故障的触发。有关更多详细信息,请参见此问题:为什么.NET将最低地址空间(非null)中的内存访问报告为NullReferenceException? - Daniel PrydenAnyObject.GetANullObject().ToString();
private object GetANullObject()
{
return null;
}
这里没有要报告的变量名!
Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.
这样的错误消息。 - O. R. Mapper我不确定,但可能是因为 .Net 不知道它是预定义类还是用户定义的类。如果它是预定义的,则可以为空(例如占用2个字节的字符串),但如果它是用户定义的,则必须创建一个实例,以便它知道此对象将占用多少内存。因此,在运行时会抛出错误。
好的,这取决于微软的工程师来回答。但是你可以使用调试器并添加观察点来找出其中哪个有问题。
然而,异常是NullReferenceException
,这意味着引用不存在。你无法获取根本未被创建的对象。
但为什么.NET不告诉我们哪个对象为空?
因为它不知道哪个对象为空。该对象根本不存在!
当我说C#编译成.NET IL代码时也是如此。.NET IL代码不知道名称或表达式,它只知道引用及其位置。同样,在这里,你无法获取不存在的内容。表达式或变量名不存在。
哲学:如果一开始没有蛋,就不能制作煎蛋卷。
很好的问题。消息框几乎没什么用。即使它被埋藏在参考定义的深处,一些类或程序集或文件或其他信息都比他们目前提供的要好(可以说是好过没有)。
您最好的选择是在带有调试信息的调试器中运行它,您的IDE会在有问题的行上断点(清楚地表明实际上是有可用的有用信息的)。