我有一些代码,当它执行时,它会抛出NullReferenceException,说:
对象引用未设置为对象的实例。
这意味着什么,我可以做什么来修复这个错误?
我有一些代码,当它执行时,它会抛出NullReferenceException,说:
对象引用未设置为对象的实例。
这意味着什么,我可以做什么来修复这个错误?
当前回答
这意味着所讨论的变量没有指向任何对象。我可以这样生成:
SqlConnection connection = null;
connection.Open();
这将引发错误,因为虽然我声明了变量“connection”,但它没有指向任何对象。当我尝试将成员称为“Open”时,没有任何引用可供它解析,它会抛出错误。
要避免此错误,请执行以下操作:
在尝试对对象执行任何操作之前,请始终初始化对象。如果您不确定对象是否为空,请使用object==null进行检查。
JetBrains的ReSharper工具将识别代码中可能出现空引用错误的每个位置,从而允许您进行空检查。这个错误是错误的头号来源,IMHO。
其他回答
这意味着所讨论的变量没有指向任何对象。我可以这样生成:
SqlConnection connection = null;
connection.Open();
这将引发错误,因为虽然我声明了变量“connection”,但它没有指向任何对象。当我尝试将成员称为“Open”时,没有任何引用可供它解析,它会抛出错误。
要避免此错误,请执行以下操作:
在尝试对对象执行任何操作之前,请始终初始化对象。如果您不确定对象是否为空,请使用object==null进行检查。
JetBrains的ReSharper工具将识别代码中可能出现空引用错误的每个位置,从而允许您进行空检查。这个错误是错误的头号来源,IMHO。
如果在保存或编译构建过程中收到此消息,只需关闭所有文件,然后打开任何文件进行编译和保存即可。
对我来说,原因是我重命名了文件,而旧文件仍然打开。
另一种可能发生NullReferenceExceptions的情况是(不正确)使用as运算符:
class Book {
public string Name { get; set; }
}
class Car { }
Car mycar = new Car();
Book mybook = mycar as Book; // Incompatible conversion --> mybook = null
Console.WriteLine(mybook.Name); // NullReferenceException
在这里,Book和Car是不兼容的类型;汽车不能转换成书。当此强制转换失败时,as返回null。在此之后使用mybook会导致NullReferenceException。
通常,应使用强制转换或,如下所示:
如果您希望类型转换总是成功的(即,您知道对象应该是什么),那么应该使用强制转换:
ComicBook cb = (ComicBook)specificBook;
如果您不确定该类型,但希望尝试将其用作特定类型,请将其用作:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
虽然导致NullReferenceExceptions的原因和避免/修复此类异常的方法已经在其他答案中得到了解决,但许多程序员尚未学会的是如何在开发过程中独立调试此类异常。
在Visual Studio中,由于Visual Studio调试器,这通常很容易。
首先,确保将捕获正确的错误-请参见如何允许在VS2010中的“System.NullReferenceException”上中断?注释1
然后从调试开始(F5)或将[VS调试器]附加到正在运行的进程。有时,使用Debugger.Break可能很有用,它将提示启动调试器。
现在,当抛出(或未处理)NullReferenceException时,调试器将在发生异常的行上停止(记住上面设置的规则吗?)。有时错误很容易被发现。
例如,在下一行中,唯一可能导致异常的代码是myString求值为null。这可以通过查看观察窗口或在即时窗口中运行表达式来验证。
var x = myString.Trim();
在更高级的情况下,例如以下情况,您需要使用上述技术之一(观察或即时窗口)来检查表达式,以确定str1是否为null或str2是否为null。
var x = str1.Trim() + str2.Trim();
一旦找到了抛出异常的位置,通常很难反向推理,以找出空值被[错误]引入的位置--
花时间了解异常原因。检查空表达式。检查之前可能导致此类空表达式的表达式。根据需要添加断点并单步执行程序。使用调试器。
1如果“抛出时中断”过于激进,并且调试器在.NET或第三方库中的NPE上停止,则可以使用“用户未处理时中断”来限制捕获的异常。此外,VS2012还引入了Just My Code,我建议您也启用它。
如果您在启用“仅我的代码”的情况下进行调试,则行为略有不同。如果启用了“仅我的代码”,调试器将忽略在“我的代码外引发的、不通过“我的码”的第一次公共语言运行时(CLR)异常
关于“我该怎么办”的问题,可以有很多答案。
在开发时防止这种错误情况的一种更“正式”的方法是在代码中应用契约设计。这意味着在开发时,需要在系统上设置类不变量,和/或甚至函数/方法前置条件和后置条件。
简而言之,类不变量确保类中有一些约束在正常使用中不会被违反(因此,类不会处于不一致的状态)。前置条件意味着作为函数/方法输入的数据必须遵循某些约束集,并且永远不会违反这些约束,后置条件意味着函数/方法输出必须再次遵循这些约束集,而永远不会违反它们。在执行无错误程序期间,不应违反合同条件,因此,在调试模式下实际检查合同设计,而在发布版本中禁用,以最大化开发的系统性能。
通过这种方式,可以避免由于违反约束集而导致的NullReferenceException情况。例如,如果在类中使用对象属性X,然后尝试调用其方法之一,并且X具有空值,则这将导致NullReferenceException:
public X { get; set; }
public void InvokeX()
{
X.DoSomething(); // if X value is null, you will get a NullReferenceException
}
但是,如果您将“属性X永远不能有空值”设置为方法前提条件,则可以防止前面描述的情况:
//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant()
{
Contract.Invariant(X != null);
//...
}
因此,.NET应用程序存在代码合同项目。
或者,可以使用断言应用契约设计。
更新:值得一提的是,这个词是Bertrand Meyer在设计Eiffel编程语言时创造的。