我有一些代码,当它执行时,它抛出一个NullReferenceException,说:
对象引用未设置为对象的实例
这意味着什么?我能做些什么来修复此错误
原因是什么
底线
您试图使用的是null(或VB.NET中的Nothing)。这意味着您要么将其设置为null,要么根本不将其设置为任何值
像其他任何东西一样,null被传递。如果在方法中为空“;“A”;,可能就是那种方法;B";将null传递给方法";A"
null可以有不同的含义:
- 未初始化的对象变量,因此不指向任何对象。在这种情况下,如果访问此类对象的成员,则会导致
NullReferenceException - 开发人员有意使用
null来表示没有可用的有意义的值。请注意,C#有变量的可空数据类型的概念(就像数据库表可以有可空字段)-您可以将null分配给它们,以表示其中没有存储值,例如int?a=零(这是Nullable<;int>;a=null;的快捷方式),其中问号表示允许在变量a中存储null。您可以使用if(a.HasValue){…}或if(a==null){…}检查这一点。可为空的变量,如本例中的a,允许通过a.value显式访问值,或通过a正常访问值
注意如果a为null,则通过a.Value访问它会引发InvalidOperationException而不是NullReferenceException——您应该事先进行检查,即如果您有另一个不可为null的变量int b然后您应该执行类似于if(a.HasValue){b=a.Value;}或更短的if(a!=null){b=a;}
本文的其余部分将更详细地介绍许多程序员经常犯的错误,这些错误可能导致NullReferenceException
更具体地说
运行时抛出一个NullReferenceException始终意味着相同的事情:您试图使用一个引用,但该引用没有初始化(或者它被初始化一次,但不再被初始化)
这意味着引用是null,您不能通过null引用访问成员(例如方法)。最简单的情况是:
字符串foo=null;
foo.ToUpper();
这将在第二行抛出一个NullReferenceException,因为您无法对指向null的字符串引用调用实例方法ToUpper()
调试
如何查找NullReferenceException的源?除了查看异常本身(将在异常发生的位置准确抛出)之外,Visual Studio中调试的一般规则也适用:放置策略断点并检查变量,方法是将鼠标悬停在变量名称上,打开一个(快速)观察窗口或使用各种调试面板,如本地和自动
如果要查找引用的设置位置或未设置位置,请右键单击其名称,然后选择";查找所有引用文件;。然后,您可以在找到的每个位置放置一个断点,并在附加调试程序的情况下运行程序。每次调试器在这样一个断点上中断时,您都需要确定是否期望引用为非null,检查变量,并验证它是否在期望时指向实例
通过以这种方式遵循程序流,您可以找到实例不应为null的位置以及未正确设置的原因
例子
可以引发异常的一些常见场景:
一般的
ref1.ref2.ref3.member
如果ref1、ref2或ref3为null,则会得到一个NullReferenceException。如果要解决此问题,请将表达式重写为更简单的等价表达式,以确定哪一个为空:
var r1=ref1;
var r2=r1.ref2;
var r3=r2.ref3;
r3.成员
具体来说,在HttpContext.Current.User.Identity.Name中,HttpContext.Current可以为null,或者User属性可以为null,或者Identity属性可以为null
间接的
公共类人员
{
公共整数{get;set;}
}
公共课堂用书
{
公共人物作者{get;set;}
}
公开课范例
{
公共图书馆
{
书b1=新书();
int authorAge=b1.Author.Age;//您从未初始化Author属性。
//没有人可以从中得到年龄。
}
}
如果要避免子(Person)null引用,可以在父(Book)对象的构造函数中初始化它
嵌套对象初始值设定项
这同样适用于嵌套对象初始值设定项:
Book b1=新书
{
作者={Age=45}
};
这意味着:
Book b1=新书();
b1.作者年龄=45岁;
当使用new关键字时,它只创建Book的新实例,而不是Person的新实例,因此作者属性仍然为null
嵌套集合初始值设定项
公共类人员
{
公共ICollection<;Book>;Books{get;set;}
}
公共课堂用书
{
公共字符串标题{get;set;}
}
嵌套集合初始值设定项的行为相同:
Person p1=新人
{
书籍={
新书{Title=";Title1";},
新书{Title=";Title2";},
}
};
这意味着:
Person p1=new Person();
p1.图书。添加(新书{Title=";Title1";});
p1.图书。添加(新书{Title=";Title2";});
newperson仅创建Person的实例,但Books集合仍为null。集合初始值设定项语法不创建集合
对于p1.Books,它只转换为p1.Books.Add(…)语句
排列
int[]number=null;
int n=数字[0];//数字为空。没有要索引的数组。
数组元素
Person[]people=新人[5];
人员[0]。年龄=20//人员[0]为空。已分配阵列,但未分配
//初始化。没有人可以设定年龄。
锯齿阵列
long[]array=new long[1][];
数组[0][0]=3;//为null,因为只有第一个维度尚未初始化。
//使用数组[0]=新长[2];第一
收藏/列表/词典
字典<;字符串,int>;agesForNames=null;
int age=agesForNames[“Bob”];//agesForNames为空。
//没有可执行查找的词典。
范围变量(间接/延迟)
公共类人员
{
公共字符串名称{get;set;}
}
var people=新列表<;人>;();
people.Add(null);
变量名称=从人员中的p选择p.名称;
string firstName=names.First();//异常在这里抛出,但实际上发生了
//在上面的线上&引用;p";是null,因为
//我们添加到列表中的第一个元素为null。
事件(C#)
公共类演示
{
公共事件事件处理程序状态已更改;
受保护的虚拟无效OnStateChanged(EventArgs e)
{
StateChanged(this,e);//这里抛出异常
//如果未附加任何事件处理程序
//声明已更改的事件
}
}
(注意:VB.NET编译器会插入事件使用情况的空检查,因此在VB.NET中不必检查事件的Nothing。)
错误的命名约定:
如果y