关于c#:对象引用未设置为对象的实例。为什么.NET不显示哪个对象为“null”?

Object reference not set to an instance of an object.Why doesn't .NET show which object is `null`?

关于此.NET未处理的异常消息:

Object reference not set to an instance of an object.

为什么.NET不显示哪个对象是null

我知道我可以检查null并解决错误。然而,为什么.NET不帮助指出哪个对象具有空引用,哪个表达式触发了NullReferenceException


(有关Visual Studio 2017中新的异常助手的信息,请参见此答案的结尾)

考虑此代码:

1
2
String s = null;
Console.WriteLine(s.Length);

这将在第二行中抛出一个NullReferenceException,您想知道为什么.net没有告诉您在抛出异常时,该异常是空的s

要理解为什么你没有得到那条信息,你应该记住,执行的不是C源,而是IL:

1
2
3
4
5
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,当评估堆栈上的第一个参数为空引用(使用ldloc.0加载的参数)时,它就会这样做。

如果.NET能够告诉它是一个空引用的s,那么它应该以某种方式跟踪评估堆栈上的第一个参数源自s。在这种情况下,我们很容易看到它是空的s,但是如果该值是另一个函数调用的返回值,而不是存储在任何变量中呢?无论如何,这种信息并不是您想要在.NET虚拟机之类的虚拟机中跟踪的。

为了避免这个问题,我建议您在所有公共方法调用中执行参数空检查(当然,除非您允许空引用):

1
2
3
4
5
public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

如果将空值传递给方法,则会得到一个异常,该异常精确描述了问题所在(s为空)。

四年后,Visual Studio 2017现在有了一个新的异常助手,该助手将尝试在抛出NullReferenceException时判断什么是空值。当方法的返回值为空时,它甚至可以提供所需的信息:

Visual Studio 2017 exception helper

注意,这只在调试版本中有效。


在以下情况下,您希望如何显示错误消息?

1
2
3
4
5
6
AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

这里没有要报告的变量名!


不确定,但这可能是因为.NET不知道它是预定义的类还是用户定义的。如果它是预先定义的,那么它可以是空的(就像占用2个字节的字符串),但是如果它是用户定义的,那么我们必须创建一个它的实例,以便它知道这个对象将占用这么多的内存。因此,它在运行时抛出错误。


好吧,这由微软的工程师来回答。但是很明显,您可以使用调试器和添加监视来找出其中哪个有问题。

但是,例外情况是NullReferenceException,这意味着引用不存在。无法获取尚未创建的对象。

but why .NET don't tell us which object is null?因为它不知道哪个对象是空的。对象根本不存在!

当我说,C编译为.NET IL代码时,情况也是如此。.NET IL代码不知道名称或表达式。它只知道参考文献及其位置。在这里,你也不能得到不存在的东西。表达式或变量名不存在。

哲学:如果你一开始没有鸡蛋,你就不能做一个赌。


好问题。这个消息框是没有用的。即使它埋在距引用定义一英里深的地方,某些类、程序集、文件或其他信息也会比它们当前提供的更好(阅读:总比什么都没有好)。

最好的选择是使用调试信息在调试器中运行它,并且您的IDE将在有问题的行中断(相当明显地表明有用的信息实际上是可用的)。