Regarding this .NET unhandled exception message:
关于这个。net未处理的异常消息:
Object reference not set to an instance of an object.
对象引用没有设置为对象的实例。
Why doesn't .NET show which object is null
?
为什么。net显示哪个对象是空的?
I know that I can check for null
and resolve the error. However, why doesn't .NET help pointing out which object has a null-reference and which expression triggered the NullReferenceException
?
我知道我可以检查null并解决这个错误。然而,为什么。net帮助指出哪个对象有一个空引用,哪个表达式触发了NullReferenceException?
5 个解决方案
#1
164
(For information about the new exception helper in Visual Studio 2017 see the end of this answer)
(关于Visual Studio 2017中的新异常助手的信息,请参见此答案的末尾)
Consider this code:
考虑这段代码:
String s = null;
Console.WriteLine(s.Length);
This will throw a NullReferenceException
in the second line and you want to know why .NET doesn't tell you that it was s
that was null when the exception was thrown.
这将在第二行中抛出一个NullReferenceException,您想知道为什么。net没有告诉您在抛出异常时是null。
To understand why you don't get that piece of information you should remember that it is not C# source that executes but rather IL:
要理解为什么你没有得到那块信息,你应该记住它不是执行的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
It is the callvirt
opcode that throws the NullReferenceException
and it does that when the first argument on the evaluation stack is a null reference (the one that was loaded using ldloc.0
).
它是抛出NullReferenceException的callvirt操作码,当评价堆栈上的第一个参数是空引用时(使用ldloc0 .0加载的参数)。
If .NET should be able to tell that it was s
that was a null reference it should in some way track that the first argument on the evaluation stack originated form s
. In this case it is easy for us to see that it is s
that was null but what if the value was a return value from another function call and not stored in any variable? Anyway, this kind of information is not what you want to keep track of in a virtual machine like the .NET virtual machine.
如果. net应该能够告诉它是年代,这是一个空引用应该以某种方式跟踪,第一个参数评价堆栈上的起源形成年代。在这种情况下,我们很容易看到它是零年代但是如果值是来自另一个函数调用的返回值,而不是存储在变量吗?不管怎样,这类信息不是你想在虚拟机中跟踪的,比如。net虚拟机。
To avoid this problem I suggest that you perform argument null checking in all public method calls (unless of course you allow the null reference):
为了避免这个问题,我建议您在所有公共方法调用中执行参数null检查(当然,除非您允许空引用):
public void Foo(String s) {
if (s == null)
throw new ArgumentNullException("s");
Console.WriteLine(s.Length);
}
If null is passed to the method you get an exception that precisely describes what the problem is (that s
is null).
如果将null传递给方法,那么您将得到一个异常,该异常精确地描述了问题所在(s为空)。
Four years later Visual Studio 2017 now has a new exception helper that will try to tell what is null when a NullReferenceException
is thrown. It is even able to give you the required information when it is the return value of a method that is null:
四年后,Visual Studio 2017现在有了一个新的异常帮助器,当一个NullReferenceException被抛出时,它将尝试告诉什么是null。当一个方法的返回值为空时,它甚至能够提供所需的信息:
Note that this only works in a DEBUG build.
注意,这只在调试版本中有效。
#2
10
How do you want the error message in the following case look like?
您希望在以下情况下的错误消息是什么样的?
AnyObject.GetANullObject().ToString();
private object GetANullObject()
{
return null;
}
No variable names to report here!
这里没有变量名报告!
#3
3
Well, that's upto engineers at Microsoft to answer. But you can obviously use a debugger and add watch to find out which of those has a problem.
这是微软的工程师们要回答的问题。但是,您显然可以使用调试器,并添加观察来找出其中的问题。
However, the exception is NullReferenceException
which means the reference does not exist . You can't get the object which hasn't been created at all.
但是,exception是NullReferenceException,这意味着引用不存在。你无法得到根本没有创建的对象。
but why .NET don't tell us which object is null?
Because it does not know which object is null. The object simply does not exist!
但是为什么。net没有告诉我们哪个对象是空的?因为它不知道哪个对象是空的。对象根本不存在!
Same is the case when I say, C# is compiled to .NET IL code. The .NET IL code does not know the names or expressions. It only knows references and their location. Here too, you cannot get what does not exist. The expression or the variable name does not exist.
同样的情况,我说,c#被编译成。net IL代码。. net IL代码不知道名称或表达式。它只知道引用和它们的位置。在这里,你也无法得到不存在的东西。表达式或变量名不存在。
Philosophy: You cannot make an omlette if you don't have an egg in the first place.
哲学:如果你一开始就没有蛋的话,你就不可能做一个omlette。
#4
1
Not sure, but this may be because .Net doesn't knows whether it is predefined class or user-defined. If it is predefined then it can be null(like string which occupies 2 Bytes) but if it is user-defined than we have to create an instance of it so that it knows that this object will occupy this much memory. So therefore it throws error at run-time.
不确定,但这可能是因为. net不知道它是预定义的类还是用户定义的。如果它是预定义的,那么它可以是null(比如字符串,占用2个字节),但是如果它是用户定义的,那么我们就必须创建它的实例,这样它就知道这个对象将占用这么多内存。因此,它在运行时抛出错误。
#5
-2
Good question. The message box is just short of useless. Even if it's buried a mile deep from the references definition, some class or assembly or file or other information would be better than what they currently provide (read: better than nothing).
好问题。这个消息框没有任何用处。即使它离引用定义有一英里深,一些类或程序集或文件或其他信息会比它们当前提供的更好(读:总比没有好)。
Your best option is to run it in the debugger with debugging information, and your IDE will break at the offending line (rather clearly demonstrating that useful information is in fact available).
您最好的选择是在调试器中使用调试信息运行它,并且您的IDE将会在违规的行中断开(很清楚地显示有用的信息实际上是可用的)。
#1
164
(For information about the new exception helper in Visual Studio 2017 see the end of this answer)
(关于Visual Studio 2017中的新异常助手的信息,请参见此答案的末尾)
Consider this code:
考虑这段代码:
String s = null;
Console.WriteLine(s.Length);
This will throw a NullReferenceException
in the second line and you want to know why .NET doesn't tell you that it was s
that was null when the exception was thrown.
这将在第二行中抛出一个NullReferenceException,您想知道为什么。net没有告诉您在抛出异常时是null。
To understand why you don't get that piece of information you should remember that it is not C# source that executes but rather IL:
要理解为什么你没有得到那块信息,你应该记住它不是执行的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
It is the callvirt
opcode that throws the NullReferenceException
and it does that when the first argument on the evaluation stack is a null reference (the one that was loaded using ldloc.0
).
它是抛出NullReferenceException的callvirt操作码,当评价堆栈上的第一个参数是空引用时(使用ldloc0 .0加载的参数)。
If .NET should be able to tell that it was s
that was a null reference it should in some way track that the first argument on the evaluation stack originated form s
. In this case it is easy for us to see that it is s
that was null but what if the value was a return value from another function call and not stored in any variable? Anyway, this kind of information is not what you want to keep track of in a virtual machine like the .NET virtual machine.
如果. net应该能够告诉它是年代,这是一个空引用应该以某种方式跟踪,第一个参数评价堆栈上的起源形成年代。在这种情况下,我们很容易看到它是零年代但是如果值是来自另一个函数调用的返回值,而不是存储在变量吗?不管怎样,这类信息不是你想在虚拟机中跟踪的,比如。net虚拟机。
To avoid this problem I suggest that you perform argument null checking in all public method calls (unless of course you allow the null reference):
为了避免这个问题,我建议您在所有公共方法调用中执行参数null检查(当然,除非您允许空引用):
public void Foo(String s) {
if (s == null)
throw new ArgumentNullException("s");
Console.WriteLine(s.Length);
}
If null is passed to the method you get an exception that precisely describes what the problem is (that s
is null).
如果将null传递给方法,那么您将得到一个异常,该异常精确地描述了问题所在(s为空)。
Four years later Visual Studio 2017 now has a new exception helper that will try to tell what is null when a NullReferenceException
is thrown. It is even able to give you the required information when it is the return value of a method that is null:
四年后,Visual Studio 2017现在有了一个新的异常帮助器,当一个NullReferenceException被抛出时,它将尝试告诉什么是null。当一个方法的返回值为空时,它甚至能够提供所需的信息:
Note that this only works in a DEBUG build.
注意,这只在调试版本中有效。
#2
10
How do you want the error message in the following case look like?
您希望在以下情况下的错误消息是什么样的?
AnyObject.GetANullObject().ToString();
private object GetANullObject()
{
return null;
}
No variable names to report here!
这里没有变量名报告!
#3
3
Well, that's upto engineers at Microsoft to answer. But you can obviously use a debugger and add watch to find out which of those has a problem.
这是微软的工程师们要回答的问题。但是,您显然可以使用调试器,并添加观察来找出其中的问题。
However, the exception is NullReferenceException
which means the reference does not exist . You can't get the object which hasn't been created at all.
但是,exception是NullReferenceException,这意味着引用不存在。你无法得到根本没有创建的对象。
but why .NET don't tell us which object is null?
Because it does not know which object is null. The object simply does not exist!
但是为什么。net没有告诉我们哪个对象是空的?因为它不知道哪个对象是空的。对象根本不存在!
Same is the case when I say, C# is compiled to .NET IL code. The .NET IL code does not know the names or expressions. It only knows references and their location. Here too, you cannot get what does not exist. The expression or the variable name does not exist.
同样的情况,我说,c#被编译成。net IL代码。. net IL代码不知道名称或表达式。它只知道引用和它们的位置。在这里,你也无法得到不存在的东西。表达式或变量名不存在。
Philosophy: You cannot make an omlette if you don't have an egg in the first place.
哲学:如果你一开始就没有蛋的话,你就不可能做一个omlette。
#4
1
Not sure, but this may be because .Net doesn't knows whether it is predefined class or user-defined. If it is predefined then it can be null(like string which occupies 2 Bytes) but if it is user-defined than we have to create an instance of it so that it knows that this object will occupy this much memory. So therefore it throws error at run-time.
不确定,但这可能是因为. net不知道它是预定义的类还是用户定义的。如果它是预定义的,那么它可以是null(比如字符串,占用2个字节),但是如果它是用户定义的,那么我们就必须创建它的实例,这样它就知道这个对象将占用这么多内存。因此,它在运行时抛出错误。
#5
-2
Good question. The message box is just short of useless. Even if it's buried a mile deep from the references definition, some class or assembly or file or other information would be better than what they currently provide (read: better than nothing).
好问题。这个消息框没有任何用处。即使它离引用定义有一英里深,一些类或程序集或文件或其他信息会比它们当前提供的更好(读:总比没有好)。
Your best option is to run it in the debugger with debugging information, and your IDE will break at the offending line (rather clearly demonstrating that useful information is in fact available).
您最好的选择是在调试器中使用调试信息运行它,并且您的IDE将会在违规的行中断开(很清楚地显示有用的信息实际上是可用的)。