Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 什么是NullReferenceException,如何修复它?_C#_.net_Vb.net_Null_Nullreferenceexception - Fatal编程技术网

C# 什么是NullReferenceException,如何修复它?

C# 什么是NullReferenceException,如何修复它?,c#,.net,vb.net,null,nullreferenceexception,C#,.net,Vb.net,Null,Nullreferenceexception,我有一些代码,当它执行时,它抛出一个NullReferenceException,说: 对象引用未设置为对象的实例 这意味着什么?我能做些什么来修复此错误?这意味着您的代码使用了设置为null的对象引用变量,即它没有引用实际的对象实例 为了防止出现错误,应该在使用可能为null的对象之前对其进行null测试 if (myvar != null) { // Go ahead and use myvar myvar.property = ... } else { // Who

我有一些代码,当它执行时,它抛出一个NullReferenceException,说:

对象引用未设置为对象的实例


这意味着什么?我能做些什么来修复此错误?

这意味着您的代码使用了设置为null的对象引用变量,即它没有引用实际的对象实例

为了防止出现错误,应该在使用可能为null的对象之前对其进行null测试

if (myvar != null)
{
    // Go ahead and use myvar
    myvar.property = ...
}
else
{
    // Whoops! myvar is null and cannot be used without first
    // assigning it to an instance reference
    // Attempting to use myvar here will result in NullReferenceException
}

这意味着所讨论的变量没有指向任何东西。我可以这样生成:

SqlConnection connection = null;
connection.Open();
这将抛出错误,因为虽然我声明了变量连接,但它没有指向任何东西。当我试图调用成员Open时,没有需要解析的引用,它将抛出错误

要避免此错误,请执行以下操作:

在尝试对对象执行任何操作之前,请始终初始化对象。 如果不确定对象是否为null,请使用object==null检查它。 JetBrains的工具将识别代码中可能出现空引用错误的每个地方,允许您进行空检查。这个错误是bug的头号来源,IMHO。

原因是什么? 要旨 您试图在VB.NET中使用null或Nothing。这意味着您要么将其设置为null,要么根本不将其设置为任何值

与其他任何东西一样,null也会被传递。如果在方法A中为null,则可能是方法B将null传递给了方法A

null可以有不同的含义:

未初始化的对象变量,因此不指向任何对象。在这种情况下,如果访问此类对象的成员,则会导致NullReferenceException。 开发人员故意使用null来表示没有可用的有意义的值。请注意,C具有可为空的数据类型的概念,例如数据库表可以有可为空的字段-您可以将null分配给它们以指示其中没有存储值,例如int?a=零;这是可为null的a=null的快捷方式;其中问号表示允许在变量a中存储null。您可以使用if a.HasValue{…}或if a==null{…}来检查这一点。可为空的变量,如本例中的a,允许显式地通过.value访问该值,或者像正常情况一样通过a访问该值。请注意,如果a为null,则通过.Value访问它会引发InvalidOperationException而不是NullReferenceException-您应该事先进行检查,即如果您有另一个不可为null的变量int b;然后,您应该执行类似于a.HasValue{b=a.Value;}的赋值,或者如果a!=空{b=a;}。 本文的其余部分将更详细地介绍并展示许多程序员经常犯的错误,这些错误可能导致NullReferenceException

更具体地说 运行时抛出NullReferenceException总是意味着相同的事情:您正在尝试使用引用,而引用未初始化,或者该引用已初始化,但不再初始化

这意味着引用为null,您不能通过null引用访问成员(如方法)。最简单的情况是:

字符串foo=null; foo.ToUpper; 这将在第二行抛出NullReferenceException,因为您不能在指向null的字符串引用上调用实例方法ToUpper

调试 如何找到NullReferenceException的源?除了查看异常本身(将在异常发生的位置准确抛出)之外,Visual Studio中调试的一般规则也适用:放置战略性断点,或者将鼠标悬停在断点的名称上,打开QuickWatch窗口,或者使用各种调试面板(如局部变量和自动变量)

如果要查找引用的设置位置或未设置位置,请右键单击其名称并选择“查找所有引用”。然后,您可以在找到的每个位置放置一个断点,并在附加调试程序的情况下运行程序。每次调试器在这样一个断点上中断时,您都需要确定是否期望引用为非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;} } 公共课堂用书 { 公众人物作者{g et;集合;} } 公开课范例 { 公共图书馆 { 书b1=新书; int authorAge=b1.Author.Age;//您从未初始化Author属性。 //没有人可以从中得到年龄。 } } 如果要避免子Person null引用,可以在父Book对象的构造函数中初始化它

嵌套对象初始值设定项 这同样适用于嵌套对象初始值设定项:

书b1=新书 { 作者={Age=45} }; 这意味着:

书b1=新书; b1.作者年龄=45岁; 使用new关键字时,它只创建Book的新实例,而不创建Person的新实例,因此属性的作者仍然为null

嵌套集合初始值设定项 公共阶层人士 { 公共ICollection图书{get;set;} } 公共课堂用书 { 公共字符串标题{get;set;} } 嵌套集合初始值设定项的行为相同:

人员p1=新人员 { 书籍={ 新书{Title=Title1}, 新书{Title=Title2}, } }; 这意味着:

人员p1=新人员; p1.Books.addNewBook{Title=Title1}; p1.Books.addNewBook{Title=Title2}; 新Person只创建Person的实例,但Books集合仍然为空。集合初始值设定项语法不创建集合 对于p1.Books,它只转换为p1.Books.Add。。。声明

大堆 int[]数字=null; int n=数字[0];//数字为空。没有要索引的数组。 数组元素 锯齿阵列 long[][]数组=新的long[1][]; 数组[0][0]=3;//为null,因为只有第一个维度尚未初始化。 //使用数组[0]=新长[2];第一 收藏/列表/词典 字典agesForNames=null; int age=agesForNames[Bob];//agesForNames为空。 //没有可执行查找的词典。 范围变量间接/延迟 公共阶层人士 { 公共字符串名称{get;set;} } var people=新列表; people.Addnull; 变量名称=从人员中的p选择p.名称; string firstName=名称。First;//异常在这里抛出,但实际上发生了 //在上面的线上。p为null,因为 //我们添加到列表中的第一个元素为null。 事件C 公开课演示 { 公共事件事件处理程序状态已更改; StateChangeDeventargs e上受保护的虚拟无效 { StateChangedthis,e;//这里抛出异常 //如果未附加任何事件处理程序 //声明已更改的事件 } } 注意:VB.NET编译器会插入事件使用情况的空检查,因此没有必要在VB.NET中对事件进行空检查

错误的命名约定: 如果您对字段的命名不同于局部变量,您可能已经意识到您从未初始化过该字段

公开课表格1 { 私人客户; 私有void Form1\u加载对象发送方,事件参数e { 客户=新客户; customer.Name=John; } 私有无效按钮\u单击对象发送者,事件参数e { MessageBox.Showcustomer.Name; } } 这可以通过遵循使用下划线作为字段前缀的约定来解决:

私人客户(u客户),; ASP.NET页面生命周期: 公共部分类问题\u编辑:System.Web.UI.Page { 保护性睾丸问题; 受保护的无效页\u加载对象发送方,事件参数e { 如果!我回来了 { //仅在第一次加载时调用,而不是在单击按钮时调用 myIssue=新的测试问题; } } 受保护的无效保存按钮\u单击对象发送者,事件参数e { myIssue.Entry=NullReferenceException here!; } } ASP.NET会话值 //如果尚未设置FirstName会话值, //然后此行将抛出NullReferenceException string firstName=会话[firstName].ToString; ASP.NET MVC空视图模型 如果在ASP.NET MVC视图中引用@Model的属性时发生异常,则需要了解在返回视图时,模型是在操作方法中设置的。从控制器返回空模型或模型属性时,视图访问该属性时会发生异常:

//控制器 公共餐厅:主控 { 公共行动结果搜索 { return视图;//忘记在此处提供模型。 } } //剃刀视图 @Model.restaurantSearch//抛出中的foreach var restaurantSearch。 { } @Model.somePropertyName

WPF控件创建顺序和事件 WPF控件是在调用InitializeComponent期间按照它们在可视化树中的显示顺序创建的。如果早期创建的控件具有事件处理程序等,并且在引用后期创建的控件的InitializeComponent期间触发,则会引发NullReferenceException

< p> 例如:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 
var address = country?.State?.County?.City;
在这里,comboBox1是在label1之前创建的。如果comboBox1\u SelectionChanged试图引用'label1',则尚未创建它

私有无效组合框1\u选择更改对象发送者,选择更改对象发送者 { label1.Content=comboBox1.SelectedIndex.ToString;//此处为NullReferenceException!! } 更改XAML中声明的顺序,即在comboBox1之前列出label1,忽略设计理念的问题,至少可以解决此处的NullReferenceException

用as铸造 var myThing=某个对象作为对象; 这不会抛出InvalidCastException,但在强制转换失败以及someObject本身为null时返回null。所以要意识到这一点

LINQ FirstOrDefault和SingleOrDefault 当没有异常时,普通版本首先抛出异常,然后单抛出异常。在这种情况下,OrDefault版本返回null。所以要意识到这一点

弗雷奇 当您尝试在空集合上迭代时,foreach将抛出。通常由返回集合的方法的意外空结果引起

List=null; 列表{}//NullReferenceException中的foreachvar v在此处 更现实的示例-从XML文档中选择节点。如果未找到节点但初始调试显示所有属性均有效,则将引发:

myData.MyXml.DocumentNode.SelectNodes//Data中的foreach变量节点 如何避免 显式检查空值并忽略空值。 如果希望引用有时为null,则可以在访问实例成员之前检查它是否为null:

无效打印名 { 如果p!=null { Console.WriteLinep.Name; } } 显式检查null并提供默认值。 期望实例的调用方法可以返回null,例如,当找不到正在查找的对象时。在这种情况下,您可以选择返回默认值:

字符串GetCategoryBook b { 如果b==null 返回未知; 返回b.类别; } 从方法调用显式检查null并引发自定义异常。 您还可以抛出自定义异常,但只能在调用代码中捕获它:

字符串GetCategorystring bookTitle { var book=library.FindBookbookTitle;//这可能返回null 如果book==null 抛出new BookNotFoundExceptionbookTitle;//自定义异常 还书.分类; } 如果值不应为null,请使用Debug.Assert,以便在异常发生之前捕获问题。 在开发过程中,如果您知道某个方法可以返回null,但永远不应该返回null,则可以使用Debug.Assert在该方法出现时尽快中断:

字符串getitleint knownBookID { //你知道这永远不会返回null。 var book=library.getbooknownbookid; //异常将发生在下一行,而不是此方法的末尾。 Debug.Assertbook!=null,库未返回已知图书ID的图书。; //其他代码 return book.Title;//在调试模式下永远不会抛出NullReferenceException。 } 通过此检查,导致在发布模式下运行时book==null时再次抛出NullReferenceException

对可为null的值类型使用GetValuerDefault,以在值为null时提供默认值。 约会时间?预约=空; Console.WriteLineAppoint.GetValuerDefaultDateTime.Now; //将显示提供的默认值DateTime.Now,因为约会为空。 约会=新日期时间2022、10、20; Console.WriteLineAppoint.GetValuerDefaultDateTime.Now; //将显示约会日期,而不是默认日期 使用空合并运算符:??[C] 或者如果[VB]。 遇到null时提供默认值的简写方法:

IService CreateServiceILogger日志,Int32?frobPowerLevel { var serviceinpl=new MyServicelog??NullLog.Instance; //请注意,也可以重写上述GetValuerDefault以使用 //合并运算符: serviceImpl.FrobPowerLevel=FrobPowerLevel±5; } 使用空条件运算符:?。或?[x]对于C 6和VB.NET 14中可用的阵列: 这有时也称为安全导航或Elvis,以其形状运算符命名。如果运算符左侧的表达式为null,则不会计算右侧的表达式,而是返回null。这意味着这样的情况:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
@foreach(var M in MyEntities){
    ...
}
var title=person.title.ToUpper; 如果此人没有标题,这将引发异常,因为它试图对具有null值的属性调用ToUpper

在C5及以下版本中,可通过以下方式进行防护:

var title=person.title==null?null:person.Title.ToUpper; 现在title变量将为null,而不是引发异常。C 6为此引入了较短的语法:

var title=person.title?.ToUpper; 这将导致title变量为null,如果person.title为null,则不会调用ToUpper

当然,您仍然需要检查nul的标题 l或将空条件运算符与空合并运算符一起使用??要提供默认值,请执行以下操作:

//常规空检查 int titleLength=0; 如果标题!=无效的 标题长度=标题长度;//如果title为null,则会引发NullReferenceException //组合“?”和“?”运算符 int titleLength=标题?长度??0; 同样,对于阵列,您可以按如下方式使用?[i]:

int[]myIntArray=null; var i=5; 智力?elem=myIntArray?[i]; 如果elem.HasValue Console.WriteLineNo值; 这将执行以下操作:如果myIntArray为null,则表达式返回null,您可以安全地检查它。如果它包含一个数组,它将执行以下操作: elem=myIntArray[i];并返回第i个元素

使用C 8中可用的空上下文: 在C8中引入的空上下文和可空引用类型对变量执行静态分析,并在值可能为空或已设置为空时提供编译器警告。可为null的引用类型允许显式允许类型为null

可以使用csproj文件中的nullable元素为项目设置nullable注释上下文和nullable警告上下文。此元素配置编译器如何解释类型的可空性以及生成哪些警告。有效设置包括:

启用:启用可为空的注释上下文。已启用可为空的警告上下文。引用类型(例如字符串)的变量不可为空。所有可空性警告均已启用。 禁用:禁用可为空的批注上下文。可为空的警告上下文已禁用。引用类型的变量是不可见的,就像早期版本的C一样。所有可空性警告都被禁用。 safeonly:启用可为空的注释上下文。可为空的警告上下文仅安全。引用类型的变量不可为null。所有安全可为空警告均已启用。 警告:可为空的批注上下文已禁用。已启用可为空的警告上下文。引用类型的变量是不可见的。所有可空性警告均已启用。 SafeOnlyWarning:禁用可为空的批注上下文。可为空的警告上下文仅安全。 引用类型的变量是不可见的。所有安全可为空警告均已启用。 可为null的引用类型使用与可为null的值类型相同的语法进行注释:A?被追加到变量的类型

调试和修复迭代器中的空derefs的特殊技术 C支持一些其他流行语言中称为生成器的迭代器块。由于延迟执行,在迭代器块中调试NullReferenceException可能特别棘手:

公共IEnumerable GetFrobsFrobFactory f,整数计数 { 对于int i=0;i //不要这样做 公共IEnumerable GetFrobsFrobFactory f,整数计数 { 如果f==null 抛出新ArgumentNullExceptionf,工厂不能为空; 对于int i=0;i 通过编写这样的空检查,可以防止NullReferenceException,但是可以将NullArgumentException移动到迭代点,而不是调用点,这对调试来说非常混乱

正确的解决方案是:

//这样做 公共IEnumerable GetFrobsFrobFactory f,整数计数 { //在抛出的公共方法中没有收益! 如果f==null 抛出新ArgumentNullExceptionf,工厂不能为空; 返回GetFrobsForRealf,count; } 私有IEnumerable GetFrobsForRealFrobFactory f,int计数 { //私有方法中的产量 Debug.Assertf!=null; 对于int i=0;i 如果检查LINQ to对象的参考源,您将看到整个过程中都使用了这种技术。编写起来稍微有点笨拙,但它使调试空性错误变得更容易。优化代码是为了方便调用者,而不是为了方便作者

关于不安全代码中空解引用的一点注记 C有一个不安全的模式,顾名思义,这是非常危险的,因为提供内存安全和类型安全的正常安全机制没有被强制执行。除非您对h有透彻而深刻的理解,否则您不应该编写不安全的代码 记忆是如何工作的

在不安全模式下,您应注意两个重要事实:

取消对空指针的引用会产生与取消对空引用相同的异常 在某些情况下,取消引用无效的非空指针可能会产生该异常 要理解为什么会这样,首先了解.NET如何生成NullReferenceException是有帮助的。这些详细信息适用于在Windows上运行的.NET;其他操作系统使用类似的机制

内存在Windows中虚拟化;每个进程都会获得一个虚拟内存空间,其中包含由操作系统跟踪的多页内存。内存的每一页上都设置了标志,以确定如何使用它:读取、写入、执行等等。如果以任何方式使用,最低的页面将标记为“生成错误”

C中的空指针和空引用在内部都表示为数字零,因此任何试图将其解引用到其相应内存存储的尝试都会导致操作系统产生错误。NET运行时随后检测到此错误并将其转换为NullReferenceException

这就是为什么同时取消对空指针和空引用的引用会产生相同的异常

第二点呢?取消引用任何落在虚拟内存最低页的无效指针会导致相同的操作系统错误,从而导致相同的异常

为什么这有意义?好吧,假设我们有一个包含两个int的结构,以及一个等于null的非托管指针。如果我们尝试取消引用结构中的第二个int,CLR将不会尝试访问位于零位置的存储;它将访问位置4的存储。但从逻辑上讲,这是一个空的解引用,因为我们是通过空地址到达那个地址的


如果您使用的是不安全的代码,并且得到了一个NullReferenceException,请注意,有问题的指针不必为null。它可以是最低页面中的任何位置,并且会产生此异常。

请注意,无论何种情况,在.NET中,原因始终是相同的:

您正在尝试使用值为Nothing/null的引用变量。当reference变量的值为Nothing/null时,这意味着它实际上并不持有对堆上存在的任何对象的实例的引用

您可能从未为变量赋值,从未创建分配给变量的值的实例,或者手动将变量设置为Nothing/null,或者调用了将变量设置为Nothing/null的函数


引发此异常的一个示例是:当您尝试检查某个内容时,该异常为null

例如:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 
var address = country?.State?.County?.City;
当您尝试对尚未实例化的内容(如上面的代码)执行操作时,.NET运行时将抛出NullReferenceException

与ArgumentNullException相比,ArgumentNullException通常作为防御措施抛出,前提是方法期望传递给它的内容不是null


更多信息请参阅

另一种情况是将空对象强制转换为空对象。例如,下面的代码:

object o = null;
DateTime d = (DateTime)o;
它将在强制转换上引发NullReferenceException。在上面的示例中似乎很明显,但在更晚绑定的复杂场景中可能会发生这种情况,其中null对象是从您不拥有的代码返回的,例如,强制转换是由一些自动系统生成的

其中一个示例是带有日历控件的简单ASP.NET绑定片段:

<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />

在这里,SelectedDate实际上是CalendarWeb控件类型的一个属性(DateTime类型),绑定可以完全返回null。隐式ASP.NET生成器将创建一段与上述强制转换代码等效的代码。这将引发一个很难发现的NullReferenceException,因为它存在于ASP.NET生成的代码中,该代码可以很好地编译…

您正在使用包含null值引用的对象。所以它给出了一个空的异常。在本例中,字符串值为null,当检查其长度时,发生异常

例如:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
异常错误为:

未处理的异常:

System.NullReferenceException:对象引用未设置为实例 指一个物体。在Program.Main

另一种可能发生NullReferenceExceptions的情况是不正确使用:

在这里,书和车是不兼容的类型;汽车不能改装成书。当此强制转换失败时,as返回null。在此之后使用mybook会导致NullReferenceException

一般来说,您应该使用cast或as,如下所示:

如果您希望类型转换总是成功的,即您知道对象应该是什么,那么您应该使用强制转换:

ComicBook cb = (ComicBook)specificBook;
如果您不确定该类型,但希望尝试将其用作特定类型,则使用as:

C8.0引入了可空引用 nce类型和不可为空的引用类型。因此,必须仅检查可为Null的引用类型,以避免出现NullReferenceException

如果尚未初始化引用类型,并且希望设置或读取其属性之一,它将抛出NullReferenceException

例如:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
您可以通过检查变量是否为null来避免这种情况:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}
要完全理解引发NullReferenceException的原因,了解和[reference Type][3]之间的区别很重要

因此,如果处理的是值类型,则不会发生NullReferenceException。尽管在处理引用类型时需要保持警惕

正如其名称所示,只有引用类型才能保存引用或直接指向nothing或“null”。而值类型总是包含一个值

参考类型必须检查这些类型:

动态 对象 一串 值类型您可以忽略以下类型:

数字类型 整型 浮点类型 十进制的 布尔 用户定义结构
另一个可能收到此异常的一般情况是在单元测试期间模拟类。无论使用何种模拟框架,都必须确保正确模拟了类层次结构的所有适当级别。特别是,被测试代码引用的HttpContext的所有属性都必须被模拟


请参阅,以获取一个比较详细的示例。

添加一个案例,即实体框架中使用的实体的类名与web表单代码隐藏文件的类名相同

假设您有一个web表单Contact.aspx,其codebehind类是Contact,并且您有一个实体名Contact

然后,当您调用context.SaveChanges时,下面的代码将抛出NullReferenceException

Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line
为了完整性,DataContext类

public class DataContext : DbContext 
{
    public DbSet<Contact> Contacts {get; set;}
}
当实体和codebehind类都在同一命名空间中时会发生错误。 要解决此问题,请重命名Contact.aspx的实体类或codebehind类

理由 我仍然不确定原因。但只要实体类中的任何一个扩展System.Web.UI.Page,就会发生此错误

讨论请看

虽然在其他答案中已经讨论了导致异常的原因和避免/修复此类异常的方法,但许多程序员尚未学会的是如何在开发过程中独立调试此类异常

在VisualStudio中,这通常很容易,这要归功于

首先,确保捕捉到正确的错误-请参阅 注1

那么要么。有时使用它可能会很有用,这将提示启动调试器

现在,当抛出或取消处理NullReferenceException时,调试器将停止,还记得上面的规则集吗?在发生异常的行上。有时错误很容易被发现

比如说,, 在下一行中,唯一可能导致异常的代码是myString的计算结果是否为null。这可以通过查看或运行中的表达式来验证

在更高级的情况下,例如以下情况,您需要使用上面的技术之一监视或即时窗口来检查表达式,以确定str1是否为null或str2是否为null

var x = str1.Trim() + str2.Trim();
一旦找到了异常抛出的位置,就可以通过反向推理找出空值[错误]引入的位置-

花点时间了解异常的原因。检查是否存在空表达式。检查以前可能导致此类空表达式的表达式。根据需要添加并逐步完成程序。使用调试器

1如果中断抛出过于激进,并且调试器在.NET或第三方库中的NPE上停止,则可用于限制捕获的异常。此外,VS2012还引入了我建议启用的功能

如果只是在启用“我的代码”的情况下进行调试,则行为会略有不同。在仅启用“我的代码”的情况下,调试器会忽略在我的代码之外抛出且不通过我的代码的第一次公共语言运行时CLR异常

当我们试图访问null对象的属性时,或者当字符串值变为空并且我们试图访问字符串方法时,会引发NullReferenceException

例如:

当访问空字符串的字符串方法时:

string str = string.Empty;
str.ToLower(); // throw null reference exception
Public Class Person {
    public string Name { get; set; }
}
Person objPerson;
objPerson.Name  /// throw Null refernce Exception 
访问空对象的属性时:

string str = string.Empty;
str.ToLower(); // throw null reference exception
Public Class Person {
    public string Name { get; set; }
}
Person objPerson;
objPerson.Name  /// throw Null refernce Exception 

我有不同的观点来回答这个问题。这类答案我还能做些什么来避免它

当跨不同层工作时,例如在MVC应用程序中,控制器需要服务来调用业务操作。在这种情况下,依赖项注入容器可用于初始化服务以避免NullReferenceException。因此,这意味着您不必担心检查null,只需从控制器调用服务,就好像它们总是可用并初始化为eithe一样 r单体或原型

public class MyController
{
    private ServiceA serviceA;
    private ServiceB serviceB;

    public MyController(ServiceA serviceA, ServiceB serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }

    public void MyMethod()
    {
        // We don't need to check null because the dependency injection container 
        // injects it, provided you took care of bootstrapping it.
        var someObject = serviceA.DoThis();
    }
}
:

其中,从对象或从System.ValueType或System.Enum类之一,或从接口类型转换为本身不可为Nullable的值类型的取消装箱转换将产生NullReferenceException

在另一个方向上,从HasValue等于false的Nullable到引用类型的装箱转换可以提供一个null引用,该引用随后可能导致NullReferenceException。经典的例子是:

DateTime? d = null;
var s = d.ToString();  // OK, no exception (no boxing), returns ""
var t = d.GetType();   // Bang! d is boxed, NullReferenceException
有时拳击以另一种方式进行。例如,使用此非泛型扩展方法:

public static void MyExtension(this object x)
{
  x.ToString();
}
以下代码将有问题:

DateTime? d = null;
d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.
出现这些情况是因为运行时在装箱可为null的实例时使用了特殊规则。

NullReference异常-Visual Basic Visual Basic的NullReference异常与C中的一样。毕竟,它们都报告在.NET Framework中定义的相同异常,并且都使用该异常。Visual Basic特有的原因很少见,可能只有一个

此答案将使用Visual Basic术语、语法和上下文。使用的示例来自大量过去的堆栈溢出问题。这是通过使用帖子中经常出现的各种情况来最大化相关性。还为那些可能需要它的人提供了更多的解释。这里很可能列出了一个与您类似的示例

注:

这是基于概念的:没有代码可以粘贴到项目中。它旨在帮助您了解是什么导致NullReferenceException NRE,如何找到它,如何修复它,以及如何避免它。NRE可能由多种方式引起,因此这不太可能是您唯一的遭遇。 堆栈溢出帖子中的示例并不总是首先显示做某事的最佳方式。 通常,使用最简单的补救方法。 基本含义 消息对象未设置为对象的实例意味着您正在尝试使用尚未初始化的对象。这可以归结为以下几点之一:

您的代码声明了一个对象变量,但它没有初始化它,而是创建一个实例或“实例化”它 您的代码假设会初始化一个对象,但实际情况并非如此 可能是其他代码过早地使仍在使用的对象无效 找到原因 因为问题是一个没有任何东西的对象引用,所以答案是检查它们以找出哪一个。然后确定它未初始化的原因。将鼠标悬停在各种变量上,Visual Studio VS将显示它们的值-罪魁祸首将是什么

您还应该从相关代码中删除任何Try/Catch块,尤其是Catch块中没有任何内容的代码。这将导致您的代码在尝试使用一个不存在的对象时崩溃。这是您想要的,因为它将确定问题的确切位置,并允许您确定导致问题的对象

捕获中的MsgBox,在。。。不会有什么帮助。这种方法还会导致非常糟糕的堆栈溢出问题,因为您无法描述实际的异常、所涉及的对象,甚至无法描述发生异常的代码行

您还可以使用“局部变量”窗口“调试->窗口->局部变量”来检查对象

一旦你知道问题是什么,问题在哪里,修复起来通常相当容易,而且比发布新问题要快

另见:

实例和补救办法 类对象/创建实例 现金出纳机 ... TextBox1.Text=注册金额'NRE 问题是Dim不创建收银器对象;它仅声明该类型的名为reg的变量。声明对象变量和创建实例是两件不同的事情

补救措施

声明实例时,通常可以使用新运算符创建实例:

Dim reg作为新的收银机“[New]创建实例,调用构造函数 “更长、更明确的形式: Dim reg As收银机=新收银机 仅适用于以后创建实例时:

私人登记为收银机“申报” ... reg=新收银机的“创建实例” 注意:不要在过程中再次使用Dim,包括新的构造函数:

私人登记为收银机 '... 公共亚新 '... Dim注册为新的收银机 端接头 这将创建一个局部变量reg,该变量仅存在于该子上下文中。您将在其他任何地方使用的模块级范围的reg变量仍然是空的

缺少新运算符是在所审查的堆栈溢出问题中看到的NullReference异常的1个原因

VisualBasic反复尝试使用New使流程清晰:使用New操作符创建一个新对象并调用Sub New(构造函数),对象可以在其中执行任何其他初始化

明确地说,Dim或Private只声明一个变量及其类型。变量的范围-是否存在于整个模块/类或i 过程的局部性-由声明的位置确定。Private | Friend | Public定义访问级别,而不是范围

有关详细信息,请参阅:

阵列 阵列还必须实例化:

专用arr as字符串 此数组仅已声明,未创建。有几种方法可以初始化数组:

Private arr as String=New String10{} ”“或者 Private arr As String=New String10{} '对于过程中的本地数组,使用'Option Inferre': Dim arr=新字符串10{} 注意:从VS 2010开始,当使用文字和选项推断初始化本地数组时,As和新元素是可选的:

Dim myDbl作为Double={1.5,2,9.9,18,3.14} Dim myDbl=新的双精度{1.5,2,9.9,18,3.14} Dim myDbl={1.5,2,9.9,18,3.14} 数据类型和数组大小是根据分配的数据推断出来的。类/模块级声明仍然需要与选项Strict一样:

私人MyDouble As Double={1.5,2,9.9,18,3.14} 示例:类对象数组

作为Foo的Dim arrFoo5 对于i作为整数=0到arrFoo.Count-1 arrFooi.Bar=i*10'异常 下一个 数组已创建,但其中的Foo对象尚未创建

补救措施

对于i作为整数=0到arrFoo.Count-1 arrFooi=新建Foo'创建Foo实例 arrFooi.Bar=i*10 下一个 使用ListofT将使没有有效对象的元素变得非常困难:

创建了Dim傻瓜作为Foo列表的新列表,但该列表为空 Dim f作为循环的Foo临时变量 对于i,整数=0到5 f=新创建的Foo'Foo实例 f、 Bar=i*10 添加到列表中的傻瓜对象 下一个 有关详细信息,请参阅:

列表和集合 NET集合,其中有许多变体-列表、字典等,也必须实例化或创建

作为字符串列表的私有myList .. myList.Addziggy'NullReference 由于相同的原因,您会得到相同的异常-仅声明了myList,但没有创建实例。补救办法是一样的:

myList=字符串的新列表 '或在声明时创建实例: 私有myList作为字符串的新列表 常见的疏忽是使用集合类型的类:

公开课Foo 私人酒吧列表作为酒吧列表 友元函数BarCount为整数 返回酒吧列表。计数 端函数 Friend Sub AddItemnewBar As酒吧 如果barList.ContainsnewBar=False,则 barList.AddnewBar 如果结束 端函数 这两个过程都将导致一个NRE,因为barList只是声明的,而不是实例化的。创建Foo的实例不会同时创建内部barList的实例。可能是为了在构造函数中执行此操作:

“公共子新”构造函数 '创建新Foo时要做的事情。。。 barList=新的条形图列表 端接头 与以前一样,这是不正确的:

公共亚新 '创建此过程本地的另一个条形图列表 将条列表变暗为新的条列表 端接头 有关详细信息,请参阅

数据提供程序对象 使用数据库提供了许多NullReference的机会,因为可以有许多对象—命令、连接、事务、数据集、DataTable、DataRows。。。。立即使用。注意:无论您使用的是哪种数据提供程序—MySQL、SQL Server、OleDB等—概念都是相同的

例1

将da调暗为OLEDB数据适配器 Dim ds作为数据集 将MaxRows设置为整数 未结 Dim sql=从tblfoobar\u列表中选择* da=新的OLEDB数据适配器SQL,con 爸爸,菲尔兹,福巴 结案 MaxRows=ds.Tablesfoobar.Rows.Count'错误 与之前一样,声明了ds数据集对象,但从未创建实例。DataAdapter将填充现有数据集,而不是创建数据集。在这种情况下,由于ds是一个局部变量,IDE警告您可能会发生这种情况:

当声明为模块/类级别变量时,就像con的情况一样,编译器无法知道对象是否由上游过程创建。不要忽略警告

补救措施

Dim ds作为新数据集 例2

ds=新数据集 da=新的OLEDB数据适配器SQL,con 地方检察官,菲尔兹,员工 txtID.Text=ds.tableEmployee.Rows0.Item1 txtID.Name=ds.tableEmployee.Rows0.Item2 打字错误是这里的一个问题:员工对员工。没有创建名为Employee的DataTable,因此尝试访问它时会出现NullReferenceException。另一个潜在的问题是假设当SQL包含WHERE子句时,会有一些可能不是这样的项

补救措施

因为这只使用一个表,所以使用表0可以避免拼写错误。检查行数。计数也有助于:

如果ds.Tables0.Rows.Count>0,则 txtID.Text=ds.table0.Rows0.Item1 txtID.Name=ds.table0.Rows0.Item2 如果结束 Fill是一个函数,返回受影响的行数,也可以进行测试:

如果da.Fillds,员工>0,则。。。 < p> 例3

将da设置为新OleDb.OleDb数据适配器选择票证。票证号, TICKET.CUSTOMER\u ID。。。从车票预订作为车票内部连接 航班详情如航班。。。其中[TICKET.TICKET\u NO]=…,con Dim ds作为新数据集 爸爸,菲尔兹 如果ds.TABLESTICKE_RESERVATION.Rows.Count>0,则 DataAdapter将提供如前一示例所示的表名,但它不会解析SQL或数据库表中的名称。因此,ds.tableTicket_保留引用了一个不存在的表

补救措施相同,请按索引参考表格:

如果ds.Tables0.Rows.Count>0,则 另见

对象路径/嵌套 如果myFoo.Bar.Items不是空的,那么 ... 代码只是测试项目,而myFoo和Bar也可能什么都不是。补救方法是一次测试一个对象的整个链或路径:

如果myFoo不是什么也不是 我的酒吧也不是什么都不是 那么myFoo.Bar.Items就不是什么了 .... 而且也很重要。一旦遇到第一个错误条件,将不会执行后续测试。这允许代码一次安全地“钻取”一个“级别”的对象,仅在确定myFoo有效后评估myFoo.Bar。对复杂对象进行编码时,对象链或路径可能会变得相当长:

myBase.myNodes3.Layer.SubLayer.Foo.Files.Addsomefilename 不可能引用空对象的任何“下游”。这也适用于以下控制:

myWebBrowser.Document.GetElementByIdformfld1.InnerText=某些值 在这里,myWebBrowser或Document可能为空,或者formfld1元素可能不存在

用户界面控件 Dim cmd5作为新的SqlCommandselect纸箱、碎片、食品条_ &来自发票,其中发票号='&_ Me.ComboBox5.SelectedItem.ToString.Trim&'和category='&'_ Me.ListBox1.SelectedItem.ToString.Trim&'和item_name='&'_ Me.ComboBox2.SelectedValue.ToString.Trim&'和到期日='&'_ Me.expiry.Text&',con 除此之外,此代码并不期望用户可能没有在一个或多个UI控件中选择某些内容。ListBox1.SelectedItem很可能为空,因此ListBox1.SelectedItem.ToString将导致NRE

补救措施

在使用数据之前验证数据,并使用选项Strict和SQL参数:

文本日期验证的Dim到期日期为“日期时间” 如果ComboBox5.SelectedItems.Count>0,则 ListBox1.SelectedItems.Count>0,然后选择 ComboBox2.SelectedItems.Count>0,也可以 DateTime.TryParseexpiry.Text,然后到期 '... 做事 其他的 MessageBox.Show…错误消息。。。 如果结束 或者,您可以使用ComboBox5.SelectedItem不是空的,也可以

Visual Basic窗体 公开课表格1 私有名称框=新文本框5{ControlsTextBox1_ ControlsTextBox2,ControlsTextBox3_ ControlsExtBox4,ControlsExtBox5_ ControlsTextBox6} “同一件事以不同的格式: 私有框列表作为来自{TextBox1,TextBox2,TextBox3…}的TextBox的新列表 “即时NRE: Private somevar As String=Me.ControlsTextBox1.Text 这是获得NRE的一种相当常见的方法。在C中,IDE将根据其编码方式报告控件在当前上下文中不存在,或者无法引用非静态成员。因此,在某种程度上,这是一种仅适用于VB的情况。它也很复杂,因为它可能导致故障级联

数组和集合不能以这种方式初始化。此初始化代码将在构造函数创建窗体或控件之前运行。因此:

列表和集合将只是空的 数组将包含五个空元素 somevar赋值将导致立即NRE,因为没有任何内容没有.Text属性 稍后引用数组元素将导致NRE。如果您在Form_Load中执行此操作,由于一个奇怪的错误,IDE可能不会在异常发生时报告异常。当代码尝试使用数组时,将弹出异常。这个无声的例外是。就我们的目的而言,关键是当在创建表单子新建或表单加载事件时发生灾难性事件时,异常可能会未报告,代码将退出过程并仅显示表单

由于在NRE之后,Sub New或Form Load事件中没有其他代码将运行,因此许多其他事情都可以保持未初始化状态

子表单加载_ '... Dim名称为String=NameBoxes2.Text'NRE ' ... '更多可能不会执行的代码 ' ... 端接头 注意:这适用于任何和所有使这些引用成为非法的控件和组件引用:

公开课表格1 Private myFiles As String=Me.OpenFileDialog1.FileName&。。。 Private dbcon As String=OpenFileDialog1.FileName&;喷射Oledb。。。 Private studentName为String=TextBox13.Text 部分补救

很奇怪,VB没有提供警告,但是 补救方法是在表单级别声明容器,但当控件确实存在时,在表单加载事件处理程序中初始化它们。只要代码在InitializeComponent调用之后,就可以在Sub New中执行此操作:

'模块级声明 作为文本框的私有名称框 Private studentName作为字符串 '表单加载、显示表单或子新建: ' '使用OP的方法非法使用OPTION STRICT nameboxs=新文本框{Me.ControlsTextBox1,Me.ControlsTestBox2。。。 studentName=TextBox32.Text'用于简单控件引用 数组代码可能还没有脱离困境。容器控件(如GroupBox或Panel)中的任何控件都不会在Me中找到。控件;它们将位于该面板或GroupBox的控件集合中。当控件名称拼写错误时,也不会返回控件TeStBox2。在这种情况下,不会在当您尝试引用它时,将产生ose数组元素和NRE

既然你知道自己在寻找什么,那么这些应该很容易找到:

按钮2位于面板上

补救措施

使用控件引用,而不是使用窗体的控件集合按名称进行间接引用:

"宣言", 作为文本框的私有名称框 '初始化-简单易读,不易出错: 名称框=新文本框{TextBox1,TextBox2。。。 '初始化列表 NamesList=文本框的新列表{TextBox1,TextBox2,TextBox3…} ”“或者 名称列表=文本框的新列表 NamesList.AddRange{TextBox1,TextBox2,TextBox3…} 函数不返回任何内容 已声明并创建作为新酒吧列表的私人酒吧 公共功能条列表作为条列表 酒吧,没问题 如果有条件的话 对于n,作为整数=0到某个值 巴兹·阿德格特巴恩 下一个 其他的 退出功能 如果结束 回程杆 端函数 在这种情况下,IDE将警告您“并非所有路径都返回值,可能会导致NullReferenceException”。您可以通过将退出函数替换为不返回任何内容来抑制警告,但这并不能解决问题。当someCondition=False时尝试使用返回的任何内容都将导致NRE:

bList=myFoo.BarList 对于bList“例外”中的每个b As条 ... 补救措施

将函数中的退出函数替换为Return bList。返回空列表与不返回任何内容不同。如果返回的对象可能为Nothing,请在使用前进行测试:

bList=myFoo.BarList 如果bList不是什么,那么。。。 执行不力的Try/Catch 执行不当的Try/Catch可能会隐藏问题所在,并导致新的问题:

将dr设置为SqlDataReader 尝试 Dim lnk As LinkButton=TryCastsender,LinkButton 作为GridViewRow的Dim gr=DirectCastlnk.NamingContainer,GridViewRow Dim eid As String=GridView1.DataKeysgr.RowIndex.Value.ToString ViewStateusername=eid sqlQry=选择名字、姓氏、部门名称、分机名称、职务名称、, 来自员工1的寻呼机,邮箱地址,其中用户名='&eid&' 如果连接。状态连接状态。打开,则 连接,打开 如果结束 command=New-SqlCommandsqlQry,连接 “更多的代码foing和barring dr=命令执行器 如果里德博士 lblFirstName.Text=Convert.ToStringdrFirstName ... 如果结束 mpe.秀 接住 最后 命令,处置 Close博士关于我该怎么办的问题,有很多答案

在开发过程中防止此类错误的一种更正式的方法是在代码中应用。这意味着您需要在开发过程中在系统上设置类不变量,甚至函数/方法的先决条件和后决条件

简言之,类不变量确保类中的某些约束在正常使用时不会被违反,因此类不会处于不一致的状态。前提条件意味着作为函数/方法输入的数据必须遵循某些约束集,并且永远不会违反这些约束集,后条件意味着操作/方法输出必须再次遵循设置的约束,而不违反这些约束。 在执行无缺陷程序的过程中,不得违反合同条件,因此,合同设计在调试模式下进行实际检查,同时在发行版中禁用,以最大限度地提高开发的系统性能

通过这种方式,您可以避免因违反约束集而导致的NullReferenceException情况。例如,如果您在类中使用对象属性X,然后尝试调用其方法之一,并且X具有空值,则这将导致NullReferenceException:

但是,如果将属性X设置为方法的前提条件,则可以防止前面描述的情况:

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant() 
{
    Contract.Invariant(X != null);
    //...
}
因此,存在针对.NET应用程序的项目

或者,合同设计可以使用

更新:It 值得一提的是,这个词是由伯特兰·迈耶创造的。

TL;DR:尝试使用Html.Partial而不是Renderpage

当我试图通过发送模型在视图中渲染视图时,对象引用未设置为对象的实例,如下所示:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
@foreach(var M in MyEntities){
    ...
}
调试显示MyOtherView中的模型为空。直到我把它改成:

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);
它成功了

此外,我之所以没有Html.Partial,首先是因为Visual Studio有时会在Html下的扭曲行中抛出错误。如果它位于不同构造的foreach循环中,则为Partial,即使它不是真正的错误:

@inherits System.Web.Mvc.WebViewPage
@{
    ViewBag.Title = "Entity Index";
    List<MyEntity> MyEntities = new List<MyEntity>();
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
}
<div>
    @{
        foreach(var M in MyEntities)
        {
            // Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
            @Html.Partial("MyOtherView.cshtml");
        }
    }
</div>
尽管我有一种感觉,那是因为Visual Studio误读了符号和括号。

简单来说:

您正在尝试访问未创建或当前不在内存中的对象

那么,如何解决这个问题:

调试并让调试器中断。。。它将直接带您到被破坏的变量。。。现在你的任务就是简单地解决这个问题。。在适当的位置使用新关键字

如果由于对象不存在而导致某些数据库命令出现此错误,则只需执行空检查并处理它:

if (i == null) {
    // Handle this
}
最难的。。如果已收集该对象。。。如果您试图使用字符串查找对象,通常会发生这种情况。。。也就是说,通过对象的名称找到它,那么GC可能已经清除了它。。。这很难找到,而且会成为一个相当大的问题。。。解决这个问题的一个更好的方法是在开发过程中的任何必要的地方进行空检查。这将节省你很多时间

通过名称查找是指一些框架允许您使用字符串查找对象,代码可能如下所示:FindObjectObjectName

你能做些什么

这里有很多很好的答案来解释什么是空引用以及如何调试它。但关于如何防止这一问题,或者至少让它更容易被发现,几乎没有什么内容

检查参数

例如,方法可以检查不同的参数,看看它们是否为null,并抛出ArgumentNullException,这显然是为了这个目的而创建的异常

ArgumentNullException的构造函数甚至将参数名和消息作为参数,这样您就可以确切地告诉开发人员问题所在

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}
使用工具

还有几个库可以提供帮助。例如,Resharper可以在编写代码时向您提供警告,尤其是在使用其属性时:

有些Microsoft代码契约使用类似契约的语法。Requiresobj!=null,它为您提供运行时和编译检查:

还有PostSharp,它允许您仅使用如下属性:

public void DoSometing([NotNull] obj)
通过这样做并使PostSharp成为构建过程的一部分,obj将在运行时被检查为null。见:

普通代码解决方案

或者,您可以始终使用普通的旧代码编写自己的方法。例如,这里有一个可以用来捕获空引用的结构。它是根据与Nullable相同的概念建模的:

NotNull隐式地转换为T和T,因此您可以在任何需要它的地方使用它。例如,可以将Person对象传递给采用NotNull的方法:

或者,您甚至可以在方法仅返回T时使用它,在本例中,通过执行强制转换来返回Person。例如,以下代码与上面的代码类似:

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}
GitHub

为了便于参考,我在GitHub上提供了上述代码,您可以在以下网址找到:

相关语言特征

C 6.0引入了空条件运算符,这有点帮助。使用此功能,可以引用嵌套对象,如果其中任何一个为null,则整个表达式将返回null

这减少了在某些情况下必须执行的空检查的数量。语法是在每个点前加一个问号。以以下代码为例:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 
var address = country?.State?.County?.City;
假设country是country类型的对象,它有一个名为State的属性,依此类推。若国家、州、县或城市为空,则地址为空。因此,您只需检查Address是否为null`

这是一个很好的特性,但它提供的信息较少。这并不能使4中的哪一个为空变得明显

内置的像可空的

C有一个很好的Nullable的简写,你可以通过在像so int?这样的类型后面加一个问号来使一些东西可以为Null


如果C有类似上面的NotNull结构,并且有类似的速记,可能是感叹号,那就太好了!这样你就可以写:public void WriteNamePerson!person.

NullReferenceException或未设置为对象实例的对象引用在您尝试使用的类的对象未实例化时发生。 例如:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 
var address = country?.State?.County?.City;
假设您有一个名为Student的类

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

现在,考虑另一个尝试重试的类。 学生的全名

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}
如上面代码所示,语句 Student s-仅声明Student类型的变量,注意Student类此时未实例化。
因此,当执行语句s.GetFullName时,它将抛出NullReferenceException

错误行对象引用未设置为对象的实例。表示尚未将实例对象指定给对象引用,但仍在访问该对象的属性/方法

例如:假设您有一个名为myClass的类,它包含一个属性prop1

现在,您正在其他类中访问此prop1,如下所示:

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref.prop1 = 1;  // This line throws an error
     }
}
上面这行抛出一个错误,因为类myClass的引用已声明,但未实例化,或者对象的实例未分配给该类的引用

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;
     }
}
要解决这个问题,您必须实例化一个对象,并将其分配给该类的引用

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;
     }
}

有趣的是,本页的答案中没有一个提到两种边缘情况:

边缘情况1:并发访问字典 NET中的泛型词典不是线程安全的,当您试图从两个并发线程访问密钥时,它们有时可能会抛出NullReference,甚至更频繁地抛出KeyNotFoundException。在这种情况下,这种例外情况很容易引起误解

边缘案例2:不安全代码 如果不安全代码引发NullReferenceException,您可能会查看指针变量,并检查它们是否为IntPtr.Zero或其他内容。这与空指针异常是一样的,但在不安全的代码中,变量通常被转换为值类型/数组等,您会感到头痛,想知道值类型如何抛出此异常

顺便说一句,除非您需要,否则不要使用不安全代码的另一个原因

边缘情况3:Visual Studio多监视器设置,辅助监视器的DPI设置与主监视器的DPI设置不同 此edge案例是特定于软件的,适用于IDE和可能的早期版本

重现此问题的方法:将工具箱中的任何组件拖动到非主监视器上的Windows窗体上,该窗体的DPI设置与主监视器不同,您将得到一个弹出窗口,其中显示“对象引用未设置为对象的实例”,这个问题已经知道了很长一段时间,在撰写本文时还没有得到解决。

这基本上是一个空引用异常。作为国家-

尝试访问时引发NullReferenceException异常 值为null的类型的成员

这是什么意思? 这意味着,如果任何成员不持有任何价值观,而我们让该成员执行某些任务,那么系统无疑会抛出一条消息并说-

“嘿,等等,该成员没有值,因此无法执行您移交给它的任务。”

异常本身表示正在引用某个内容,但未设置其值。因此,这表示它仅在使用引用类型时发生,因为值类型不可为null

如果使用值类型成员,则不会发生NullReferenceException

class Program
{
    static void Main(string[] args)
    {
        string str = null;
        Console.WriteLine(str.Length);
        Console.ReadLine();
    }
}
上面的代码显示了一个简单的字符串,该字符串被赋值为空值

现在,当我尝试打印字符串str的长度时,确实会收到一条未处理的“System.NullReferenceException”类型的异常消息,因为成员str指向null,并且不能有任何长度的null

当我们忘记实例化引用类型时,也会发生“NullReferenceException”

假设我有一个类和成员方法。我没有实例化我的类,只是命名了我的类。现在,如果我尝试使用该方法,编译器将抛出错误或发出警告,具体取决于编译器

class Program
{
    static void Main(string[] args)
    {
        MyClass1 obj;
        obj.foo();  // Use of unassigned local variable 'obj'
    }
}

public class MyClass1
{
    internal void foo()
    {
        Console.WriteLine("Hello from foo");
    }
}
上述代码的编译器会引发一个错误,即变量obj未赋值,这表示我们的变量有空值或无值。上述代码的编译器会引发一个错误,即变量obj未赋值,这表示我们的变量有空值或无值

为什么会发生这种情况? NullReferenceException是由于我们没有检查对象的值而导致的。在代码开发中,我们经常不检查对象值

当我们忘记实例化我们的对象时,它也会出现。使用可返回或设置空值的方法、属性、集合等也可能导致此异常

如何避免呢? 有多种方式和方法可以避免这种著名的例外情况:

显式检查:我们应该坚持检查对象、属性、方法、数组和集合是否为null的传统。这可以简单地使用if-else等条件语句实现

异常处理:管理此异常的重要方法之一。使用简单的try-catch-finally块,我们可以控制此异常并维护其日志。这在以下情况下非常有用: 您的应用程序正在生产阶段

空运算符:在设置对象、变量、属性和字段的值时,还可以方便地使用空合并运算符和空条件运算符

调试器:对于开发人员来说,我们拥有调试的强大武器。如果我们在开发过程中遇到NullReferenceException,我们可以使用调试器找到异常源

内置方法:系统方法,如GetValueOrDefault、IsNullOrWhiteSpace和IsNullorEmpty,检查是否为null,如果存在null值,则分配默认值

这里已经有很多很好的答案。您还可以通过我的网站上的示例查看更详细的说明


希望这也有帮助

如果在保存或编译版本时收到此消息,只需关闭所有文件,然后打开任何要编译和保存的文件即可


对我来说,原因是我已经重命名了文件,而旧文件仍然打开。

修复NullReferenceExeption最简单的方法有两种

例如,如果您有一个附加了脚本的游戏对象和一个名为rb rigidbody的变量,那么当您启动游戏时,该变量将以null开头。 这就是为什么会得到NullReferenceExection,因为计算机没有存储在该变量中的数据

我将使用刚体变量作为示例。 我们可以通过以下几种方式轻松添加数据:

使用AddComponent>Physical>RigidBody将刚体添加到对象中 然后进入脚本并键入rb=GetComponent; 这行代码在Start或Awake函数下工作得最好。 您可以通过编程方式添加组件,同时使用一行代码分配变量:rb=AddComponent; 进一步说明:如果您想向对象添加组件,但可能忘记了添加组件,则可以在类声明上方键入[RequiredComponentTypeOfRigidBody],在所有用法下方的空格中键入[RequiredComponentTypeOfRigidBody]


享受并享受制作游戏的乐趣

您可以在C 6中使用Null条件运算符以干净的方式修复NullReferenceException,并编写更少的代码来处理Null检查

它用于在执行成员访问?之前测试null?。还是索引?[操作]

实例 这相当于:

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }
结果是,当p为null或p.party为null时,名称将为null

否则,变量名将被分配p.party.FirstName的值


有关更多详细信息:

-1:因为问题是什么是NullReferenceException,值类型不相关。@约翰·桑德斯:我不同意。作为一名软件开发人员,能够区分值和引用类型非常重要。否则人们最终会检查整数是否为null。是的,只是不在本问题的上下文中n、 谢谢你的提示。我对它做了一些改进,并在顶部添加了一个示例。我仍然认为提及引用和值类型是有用的。我认为你没有添加其他答案中没有的任何内容,因为这个问题假设了引用类型。多么深刻!我从未将“null”常量视为引用值。所以这就是C abstrac的方法一个Null指针HU.B/C,如我在C++中所记得的,NPE可以通过取消未初始化的指针IE,REF类型而在C中引起,其默认值恰好是一个未分配给该进程的地址,很多情况下,这将是0,特别是在C++的后来的自动初始化的版本中,它属于OS- F和它的B。eeotch或只是捕获操作系统攻击进程的sigkill。当变量出现时,这种情况会发生很多。我发现在我更改UI元素的类型后,这种情况经常发生在事件处理程序中,但我忘记了更新后面的代码。-1:这只处理单个场景-未初始化的依赖项。这是NullReference的少数场景ceException。大多数情况下都是对对象如何工作的简单误解。其次最常见的情况是开发人员假设对象将自动初始化的其他情况。通常不使用依赖项注入来避免NullReferenceException。我相信您在这里没有找到一般情况。在任何情况下,如果您将答案编辑为更符合的样式,则我将删除下一票。也许这是一个愚蠢的评论,但避免此问题的第一个也是最好的方法不是初始化对象吗?对于我来说,如果发生此错误,通常是因为我忘了初始化类似数组元素的内容。我认为定义此项不太常见将对象设为null,然后引用它。也许可以给出解决描述附近的每个问题的方法。这仍然是一篇好文章。如果没有对象,而是方法或属性的返回值,该怎么办?book/author示例有点奇怪……它是如何编译的?intellisense是如何工作的?这是什么?我不好我上一次的编辑有帮助吗?如果没有,那么请更明确地说明什么是

“难道你不认为这是个问题吗?”约翰森说,“哦,不,对不起,我是指对象初始值设定项版本。”。新书{Author={Age=45}};内部初始化是如何实现的。。。我想不出有哪种情况下内部init可以工作,但它可以编译并且intellisense可以工作。。。除非是structs?我想添加这个,因为没有人提到这个,而且就其作为一种方法的存在而言,我的目的是丰富这个主题。谢谢你丰富这个主题。我已经对你的补充提出了我的意见。现在其他人也可以这么做了。我认为这是一个值得添加到主题中的内容,因为这是一个被高度评价的主题。我以前听说过代码合同,这是一个很好的提醒,可以考虑使用它们。避免的一种线性方法:DateTime x=DateTime o作为DateTime??默认值;这是不正确的。String.Empty.ToLower不会引发空引用异常。它表示一个实际字符串,尽管是空字符串,即。因为这有一个要调用ToLower的对象,所以在那里抛出null引用异常是没有意义的。您想要的是Html.Partial,而不是@Html.partialso,请说明哪一行抛出了异常,以及原因。错误发生在MyOtherView.cshtml中,我在这里没有包括它,因为模型没有正确发送,它是null,所以我知道错误在于我发送模型的方式。如果你有一个对象的引用,那么GC永远不会清除它。如果你使用像FindObjectName这样的对象,GC将无法事先知道你将引用该对象。。这就是我试图解释的。。这些发生在运行时有一些框架在C中提供此功能,例如Unity。这个问题与BCl无关。在批评之前先搜索一下互联网,有很多类似的功能,我甚至每天都在使用这些功能。现在请告诉我这个答案怎么没有意义。检查链接并更正您自己。专家先生:p我在您的链接中看到的示例分配GameObject的结果。查找到成员字段。这是一个引用,GC在收集包含的对象之前不会收集它。永远不要抛出NullReferenceException@JohnSaunders我敢问为什么?说真的,为什么?NullReferenceException是由CLR抛出的。这意味着发生了对null的引用。这并不意味着会出现对null的引用,除非您先进行了巧妙的检查。我明白您的观点,这会让人感到困惑。对于这个例子,我已经将它更新为一个常规异常,在GitHub中更新为一个自定义异常。对于这样一个基本问题,这是一个很好的答案。当你的代码失败时,情况并不那么糟糕。当它来自您所依赖的某个商业第三方库的深处,并且客户支持部门一直坚持认为是您的代码导致了问题时,这是非常可怕的。而且你也不完全确定这不是真的,整个项目都要停止了。。事实上,我认为这可能为我的墓碑留下一个合适的墓志铭:对象引用未设置为对象的实例。VS 2017中的异常帮助器将更有助于诊断此异常的原因-在新的异常帮助器下。亲爱的未来访客,此问题的答案同样适用于异常。如果您的问题已作为此问题的副本关闭,并且您遇到了一个ANE,请按照答案中的说明进行调试并修复您的问题。@只有将null作为参数传递时,才会出现ANE。你能举一个例子,如果一个电子问题作为这个问题的重复关闭吗?它出现在元,但我必须去挖掘链接。但是对于那个注释,一个ANE只是一个NRE,但是有人添加了一个先发制人的检查,并且你至少知道提供的参数名是空的,所以它比一个直接的NRE更容易诊断。你的字典示例不是一个edge案例。如果对象不是线程安全的,那么从多个线程使用它会产生随机结果。您的不安全代码示例在哪方面不同于null?JetBrains的Resharper工具将识别代码中可能存在null引用错误的每个位置。这是不正确的。我有一个没有检测的解决方案,但是代码偶尔会导致异常。我怀疑当涉及多线程时,它偶尔是不可检测的(至少他们是这样),但我不能进一步评论,因为我还没有确定错误的位置。但是当NullReferenceException出现在usign HttpContext.Current.Response.Clear中时,如何解决它呢。上述任何解决方案都无法解决此问题。因为在创建HttpContext的对象时出现了一个错误,重载解析失败,因为没有可访问的“New”接受此数量的参数。
  var name = p?.Spouse?.FirstName;
    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }