关于这个.NET未处理的异常消息:
对象引用未设置为对象的实例。
为什么.NET不显示哪个对象是null
?
我知道我可以检查null
并解决错误。但是,为什么.NET没有帮助指出哪个对象具有空引用以及哪个表达式触发了NullReferenceException
?
答案 0 :(得分:168)
(有关Visual Studio 2017中新异常帮助程序的信息,请参阅本答案的结尾)
考虑以下代码:
String s = null;
Console.WriteLine(s.Length);
这将在第二行中抛出一个NullReferenceException
,你想知道为什么.NET没有告诉你,当抛出异常时它是s
为空。
要理解为什么你没有得到这些信息,你应该记住它不是执行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
,当评估堆栈上的第一个参数是空引用(使用ldloc.0
加载的那个)时,它会执行此操作。
如果.NET应该能够告诉它s
是一个空引用,它应该以某种方式跟踪评估栈上的第一个参数来自s
。在这种情况下,我们很容易看到s
为null,但如果该值是来自另一个函数调用的返回值并且未存储在任何变量中,该怎么办?无论如何,这种信息并不是你想要在.NET虚拟机之类的虚拟机中跟踪的。
为避免此问题,我建议您在所有公共方法调用中执行参数null检查(当然,除非您允许空引用):
public void Foo(String s) {
if (s == null)
throw new ArgumentNullException("s");
Console.WriteLine(s.Length);
}
如果将null传递给该方法,则会得到一个异常,该异常可以准确地描述问题所在(s
为空)。
四年后,Visual Studio 2017现在有了一个新的异常助手,它将尝试在抛出NullReferenceException
时告诉什么是null。当它是null的方法的返回值时,它甚至能够为您提供所需的信息:
请注意,这仅适用于DEBUG构建。
答案 1 :(得分:9)
您希望以下情况中的错误消息如何?
AnyObject.GetANullObject().ToString();
private object GetANullObject()
{
return null;
}
此处无需报告变量名称!
答案 2 :(得分:1)
嗯,微软的工程师可以回答这个问题。但是显然你可以使用调试器并添加手表来找出哪些有问题。
但是,例外是NullReferenceException
,这意味着引用不存在。您根本无法获取尚未创建的对象。
but why .NET don't tell us which object is null?
因为它不知道哪个对象为null。该对象根本不存在!
当我说,C#被编译为.NET IL代码时也是如此。 .NET IL代码不知道名称或表达式。它只知道引用及其位置。在这里,你也无法得到不存在的东西。表达式或变量名称不存在。
哲学:如果你没有鸡蛋,你就不能制作一个鸡蛋。
答案 3 :(得分:1)
不确定,但这可能是因为.Net不知道它是预定义的类还是用户定义的。如果它是预定义的,则它可以为null(如占用2个字节的字符串),但如果它是用户定义的,则必须创建它的实例,以便它知道该对象将占用这么多的内存。因此它在运行时抛出错误。
答案 4 :(得分:-2)
好问题。消息框没有用处。即使它埋在参考定义的一英里深处,某些类或汇编或文件或其他信息也会比它们目前提供的更好(阅读:总比没有好)。
您最好的选择是在调试器中使用调试信息运行它,并且您的IDE将在违规行中断开(相当清楚地证明实际可用的有用信息)。