对象引用未设置为对象的实例。.NET为什么不显示哪个对象为null?


103

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

你调用的对象是空的。

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

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

c#  .net 

2
发生这种情况时,请重写发生的行,以便首先检查每个可能的结果是否为null-然后您将确切知道它是什么。要么,要么附加了Visual Studio令人惊叹的调试器,该调试器会在发生异常的那
一刻

5
并非如此,他只是问为什么.NET框架不能帮助程序员显示哪个对象为null。我想这是性能损失(您需要反思)。但我也不确定。
2013年

1
@bas:虽然这是对的,但这个问题有点令人误解,因为它应该询问的是“表达式的一部分”,而不是“对象”。这也解释了为什么单纯的反射将无济于事,但是将需要一些广泛的调试信息。
OR Mapper

4
我仍然对答案感到好奇。有点类似于.net异常,它无法指出字典中不存在哪个键。另外,我不理解这个问题的奉献者。
bas

12
请使用术语:对象永远不能为null。但是可能有一个对象引用。但是对象引用只是内存中的一个位置-除非您仍然连接了调试器,否则它对您有什么帮助?
Oskar Berggren

Answers:


169

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


考虑以下代码:

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

这将NullReferenceException在第二行中引发a ,您想知道为什么s在引发异常时.NET不会告诉您那是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:调用System.Console.WriteLine

当评估堆栈上的第一个参数为空引用(使用加载的参数)时,它是callvirt引发的操作码。NullReferenceExceptionldloc.0

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


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

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

如果将null传递给该方法,则将获得一个准确描述问题所在的异常(即snull)。


四年后,Visual Studio 2017现在拥有一个新的异常帮助器,它将尝试在NullReferenceException抛出a时判断是否为null 。当方法的返回值为null时,它甚至可以为您提供所需的信息:

Visual Studio 2017异常帮助器

请注意,这仅在DEBUG构建中有效。


5
行号和源文件名不是存储在IL代码本身中,还是它们?但是,它们可以用于调试。
OR Mapper

4
@MartinLiversage:是的。因此,问题归结为:为什么符号文件中没有存储足够的信息,这些信息也告诉代码中的表达式被评估为null
OR Mapper

2
@MartinLiversage:可以通过某种方式访问​​符号文件,以便在出现异常以及异常消息时,可以在调试器的输出中显示源文件和行号。因此,问题是,为什么不包括有关确切返回的内容的更多信息的原因是什么?null请注意,OP并不声称他或她想知道对于发布版本以及调试版本可能就足够了。
OR Mapper

3
嗯,我们不应该忘记实际执行的不是IL,而是在运行时从中构建的本机代码。
Oskar Berggren

2
@MartinLiversage:这个问题中没有人声称我们希望为发行版本提供完美的支持。无论如何,我不太了解将使用对象引用的IL操作码(可能是null)与返回该对象引用的源文件行和列相关联的问题。
OR Mapper

9

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

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

没有变量名称可在此处报告!


2
我怀疑OP正在源代码中寻找返回null而不是对象的表达式。我已在问题中分别添加了评论,希望他或她能解决问题。如果我的猜想是正确的,则OP会期望出现类似Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.错误消息的信息。
OR Mapper

1
@ORMapper我同意。如果我有足够的声誉点可以添加评论,那么我只会在OP中添加我的“答案”!
romar

1
“尝试调用XYZ类的ToString()方法的空引用”将比我们现在获得的帮助更大。
Michael Levy 2015年

最有用的是堆栈跟踪,该跟踪在每个调用级别显示在哪个文件中的哪一行中导致了错误。哦,等等...这就是现在所做的!
Jim Balter

1

好吧,这取决于微软的工程师。但是您显然可以使用调试器并添加监视功能,以找出其中哪些有问题。

但是,例外情况是NullReferenceException该引用不存在。您根本无法获得尚未创建的对象。

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

我说的是将C#编译为.NET IL代码,情况也是如此。.NET IL代码不知道名称或表达式。它只知道引用及其位置。在这里,您也无法获得不存在的内容。表达式或变量名不存在。

哲学:如果您一开始没有鸡蛋,就不能制作蛋卷。


3
这也不是一个答案:)
bas

如果参考不存在,如何获得参考?@Bas
Aniket Inge

4
“嗯,这取决于微软的工程师。” 因此,让他们阐明这一点,而不要说明显而易见的事实
bas

@bas我们至少可以决定什么是逻辑。从逻辑上讲该对象不存在。您将如何捕获到异常,然后打印出对象名称?它只是不存在。甚至没有堆栈..
Aniket英格

因此,答案是几乎不可能指出哪个对象具有nullreference?这也是答案。我并不是说我知道,我只是喜欢这个问题:)。+1:p
bas

1

不确定,但这可能是因为.Net不知道它是预定义的类还是用户定义的。如果它是预定义的,则它可以为null(如占用2个字节的字符串),但是如果它是用户定义的,则我们必须为其创建一个实例,以便它知道该对象将占用这么多的内存。因此,它在运行时引发错误。


-2

好问题。消息框几乎没有用。即使它离引用定义很深,也有些类,程序集或文件或其他信息会比其当前提供的更好(读:总比没有好。)

最好的选择是在调试器中使用调试信息运行它,并且您的IDE会在违规行处中断(而显然表明实际上有用的信息是可用的)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.