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的令人惊异的调试器,它会在发生异常时立即中断,并让您看到什么是空的:)
- 这不是他的问题
- 在programmers programmers.stackexchange.com/questions/130250/…
- 有很多方法可以检查在点网中的错误可能是你不知道所有那些………其中之一是"错误列表"。当您自动调试代码时,它将显示出现问题的行。
- 实际上,他只是问为什么.NET框架不能帮助程序员显示哪个对象是空的。我想这是对表演的惩罚(你需要反思)。但我也不确定。
- @巴斯:虽然这是真的,但这个问题有点误导人,因为它应该问的是"表达式的一部分",而不是"对象"。这也解释了为什么仅仅反射不会有帮助,但需要一些广泛的调试信息。
- 不过,我还是很想知道答案。它类似于.NET异常,不帮助指出字典中不存在哪个键。另外,我不明白对这个问题的投入。
- 感谢所有的投票
- +1为了它的价值
- .NET是一个框架。它应该怎么说?你是说Visual Studio吗?
- 术语请:对象永远不会为空。但对象引用可能是。但是一个对象引用只是内存中的一个位置——它将如何帮助您,除非您连接了一个调试器?
- @vladl:Visual Studio对于异常或调试器消息并没有那么多的功能。没有Visual Studio,.NET就可以完美地使用。关键的一点是,NullPointerException及其消息是由.NET框架本身中的代码提供的,因此在这里询问.NET框架的功能是正确的。
- 是否可以通过clr的开销找到空变量的名称?
- @Jasd:它不一定是单个变量;它也可以是更复杂的表达式。如myTuples[4].Item1。
- 100%重复stackoverflow.com/questions/779091/…
- @SPAJCE:这个问题和这个问题之间唯一的联系就是它们都引用了相同的异常。问题完全不同。
- 哈哈,又一个很酷的问题。+ 1
- @阿夫辛默拉巴尼谢谢
- "它有点类似于.NET异常,但没有帮助指出字典中不存在哪个键。"——实际上,它一点也不相似。如果一个键不在字典中,那么这个键就在手边,可以包含在异常中。对于空引用,手头上唯一的东西就是空。
(有关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时判断什么是空值。当方法的返回值为空时,它甚至可以提供所需的信息:
注意,这只在调试版本中有效。
- 当我问O.R.Mapper他打算从哪里获取源代码时,我的观点正是如此。
- 现在这是一个答案:)
- 行号和源文件名也没有存储在IL代码本身中,还是它们?但是,它们可以用于调试。
- @O.R.Mapper:它们存储在符号(.pdb文件)中。
- @马丁利弗萨:没错。所以问题可以归结为:为什么符号文件中没有足够的信息来存储,这些信息还可以告诉代码中的表达式被计算为null。
- @O.R.Mapper:符号文件由调试器在调试期间使用,而.NET虚拟机根本不使用。在一个版本中,构建优化将改变生成的代码,使得很难准确地将IL指令与源代码关联起来。执行.NET应用程序不需要符号。
- @martindeliversage:可以通过某种方式访问符号文件,以便在发生异常时,连同异常消息一起,源文件和行号可以显示在调试器的输出中。所以,问题是,为什么不包括关于返回的null的更多信息-请注意,OP并不声称他或她想知道,对于发布版本,调试版本可能就足够了。
- 嗯,我们不应该忘记,实际上执行的不是IL,而是在运行时根据它构建的本机代码。
- @oskarberggren:我在这里推测,但我希望callvirt操作码转换为执行0检查的本机代码,而不是依赖底层硬件和操作系统的任何异常机制(例如,不会引发一般保护故障)。
- @O.R.Mapper:符号文件是一个调试器功能,不能很好地用于版本构建。符号将IL操作码与源文件行相关联,并为方法和变量提供人类可读的名称,但它不会告诉您堆栈上的特定值为何为空引用的任何信息。我不是说这是不可能的,但我认为在一般情况下,这是一个难以解决的问题。任何生产应用程序都不会有任何符号。
- @MartinLiverSage:在这个问题上,没有人声称我们希望在发布版本中完全支持它。无论如何,我不太明白将使用对象引用(可能是null)的IL操作码与返回该对象引用的源文件行和列相关联的问题。
- @O.R.Mapper没有足够的信息来说明表达式计算结果为空的原因是…还没有人给它编码!想想这会有多困难,你需要写多少个角落的案例,所有的测试,以确保它永远不会失败,崩溃.net,等等,为了那么少的收益。
- @Patashu:我目前无法想象任何角落的情况,因为总是有一个返回null的表达式,但是如果"它没有实现"是我们在这里能找到的唯一原因(除非有人知道实际设计决策的原因),那么应该添加它作为答案。网络崩溃的风险是…正如上面其他人指出的那样,"完全不被.NET虚拟机使用"的东西有点牵强。
- @martindeliversage:事实上,afaik的NullReferenceException的实现确实依赖于所引发的硬件故障。有关详细信息,请参见此问题:为什么.NET报告最低地址空间(非空)中的内存访问为NullReferenceException?
- @O.R.Mapper:作为一个例子,使用异步、yield等等会产生与程序员实际编写的代码截然不同的编译器创建的代码。
- @帕塔苏:你能提供一个实际的例子,在哪里会有问题吗?一个yield someObject.SomeProperty.SomeNestedProperty.SomeValue;语句可以假定编译成MoveNext()方法中的current = someObject.SomeProperty.SomeNestedProperty.SomeValue;之类的东西,对吗?这个表达式的单个步骤是否与yield语句的外部步骤有任何不同?
- @O.R.Mapper Async/Waitter状态机就是一个很好的例子——如果你曾经读过异步是如何在C语言中实现的,那么简单的方法会被翻译成一个巨大的、复杂的状态机——为了让编译器生成的表达式/变量有意义,它必须存储足够的信息来执行反向操作并获取BA。点击源代码。在任何情况下,都必须有人来写。
- 我理解为什么运行时没有告诉我哪个引用是空的,但是它不能告诉我调用了哪个方法导致了错误吗?在您的示例中,如果异常告诉我调用了length方法,这将是非常有用的信息。
- 实际上,对于上面的示例,运行时没有足够的信息来提供错误消息,比如"试图调用类System.String的长度方法的空引用"。这将是一条更有用的消息,运行时似乎可以提供这些细节。
- "它不能告诉我调用哪个方法导致了错误吗?"--调试器提供调用发生的文件名和行号。这就是你所需要的。
在以下情况下,您希望如何显示错误消息?
1 2 3 4 5 6
| AnyObject.GetANullObject().ToString();
private object GetANullObject()
{
return null;
} |
这里没有要报告的变量名!
- 我怀疑OP正在源代码中查找返回空值的表达式,而不是对象。我已经对这个问题添加了各自的评论,希望他或她能澄清问题。如果我的怀疑是正确的,操作人员会期望像Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.这样的东西作为错误信息。
- @O.R.Mapper我同意。如果我有足够的信誉点添加评论,我会把我的"答案"放在评论中。
- "试图调用类xyz的toString()方法的空引用"比我们现在得到的更有用。
- 最有用的是一个堆栈跟踪,它在每个调用级别上精确显示导致错误的文件中的哪一行。哦,等等…现在就是这样!
不确定,但这可能是因为.NET不知道它是预定义的类还是用户定义的。如果它是预先定义的,那么它可以是空的(就像占用2个字节的字符串),但是如果它是用户定义的,那么我们必须创建一个它的实例,以便它知道这个对象将占用这么多的内存。因此,它在运行时抛出错误。
好吧,这由微软的工程师来回答。但是很明显,您可以使用调试器和添加监视来找出其中哪个有问题。
但是,例外情况是NullReferenceException,这意味着引用不存在。无法获取尚未创建的对象。
but why .NET don't tell us which object is null?因为它不知道哪个对象是空的。对象根本不存在!
当我说,C编译为.NET IL代码时,情况也是如此。.NET IL代码不知道名称或表达式。它只知道参考文献及其位置。在这里,你也不能得到不存在的东西。表达式或变量名不存在。
哲学:如果你一开始没有鸡蛋,你就不能做一个赌。
- 这也不是答案:)
- 如果引用不存在,如何获取它?@ Bas
- "好吧,这得由微软的工程师来回答。"。所以,让他们对此有所了解,而不是说
- @我们至少可以决定什么是合乎逻辑的。从逻辑上讲,对象不存在。如何捕获一个异常,然后打印出对象的名称?它只是不存在。即使在堆栈上也不行。
- 所以答案是,这实际上不可能指出哪个对象有空引用?这也是一个答案。我不是说我知道,我只是喜欢这个问题。+所有努力:P
- @Aniket:我怀疑OP正在源代码中查找返回null的表达式,而不是对象。我已经对这个问题添加了各自的评论,希望他或她能澄清问题。
- @O.R.Mapper,他打算从哪里得到这个表达?
- @运行时调试信息?这并不是不可能的,它只是看起来当前的实现没有做到这一点,而OP只是在问为什么没有。
- 它实际上不是空的对象,而是引用。引用不存在。
好问题。这个消息框是没有用的。即使它埋在距引用定义一英里深的地方,某些类、程序集、文件或其他信息也会比它们当前提供的更好(阅读:总比什么都没有好)。
最好的选择是使用调试信息在调试器中运行它,并且您的IDE将在有问题的行中断(相当明显地表明有用的信息实际上是可用的)。