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

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

这意味着什么,我可以做什么来修复这个错误?


当前回答

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


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

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

调试显示模型在MyOtherView中为Null。直到我将其更改为:

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

它奏效了。

此外,我没有Html.Paraal的原因是,Visual Studio有时会在Html.Partial下抛出看起来像错误的曲线,如果它位于不同构造的foreach循环中,即使它不是真正的错误:

@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>

但我能够运行应用程序,没有出现任何问题。通过将foreach循环的结构更改为如下所示,我能够消除该错误:

@foreach(var M in MyEntities){
    ...
}

虽然我觉得这是因为Visual Studio误读了符号和括号。

其他回答

Simon Mourier举了一个例子:

object o = null;
DateTime d = (DateTime)o;  // NullReferenceException

其中,从对象(或从System.ValueType或System.Enum类之一,或从接口类型)到值类型(非Nullable<>)的拆箱转换(强制转换)本身会产生NullReferenceException。

在另一个方向上,从HasValue等于false的Nullable<>到引用类型的装箱转换可能会给出一个空引用,然后会导致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.

出现这些情况是因为运行时在装箱Nullable<>实例时使用的特殊规则。

另一个可能收到此异常的一般情况是在单元测试期间模拟类。无论使用何种模拟框架,您都必须确保正确模拟类层次结构的所有适当级别。特别是,被测试代码引用的HttpContext的所有财产都必须进行模拟。

请参阅“测试自定义AuthorizationAttribute时引发的NullReferenceException”,以获取一些详细的示例。

关于“我该怎么办”的问题,可以有很多答案。

在开发时防止这种错误情况的一种更“正式”的方法是在代码中应用契约设计。这意味着在开发时,需要在系统上设置类不变量,和/或甚至函数/方法前置条件和后置条件。

简而言之,类不变量确保类中有一些约束在正常使用中不会被违反(因此,类不会处于不一致的状态)。前置条件意味着作为函数/方法输入的数据必须遵循某些约束集,并且永远不会违反这些约束,后置条件意味着函数/方法输出必须再次遵循这些约束集,而永远不会违反它们。在执行无错误程序期间,不应违反合同条件,因此,在调试模式下实际检查合同设计,而在发布版本中禁用,以最大化开发的系统性能。

通过这种方式,可以避免由于违反约束集而导致的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编程语言时创造的。

更新C#8.02019:可为空的引用类型

C#8.0引入了可为null的引用类型和不可为null引用类型。因此,必须只检查可为null的引用类型,以避免出现NullReferenceException。


如果尚未初始化引用类型,并且希望设置或读取其财产之一,则会抛出NullReferenceException。

例子:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

您可以通过检查变量是否为空来避免这种情况:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

要完全理解为什么会引发NullReferenceException,必须了解值类型和[引用类型][3]之间的区别。

因此,如果要处理值类型,则不能出现NullReferenceExceptions。尽管在处理引用类型时需要保持警惕!

正如名称所暗示的那样,只有引用类型可以保存引用或直接指向任何对象(或“null”)。而值类型始终包含值。

引用类型(必须选中这些类型):

动态对象一串

值类型(您可以忽略这些类型):

数字类型整数类型浮点类型十进制的布尔用户定义的结构

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

边缘案例#1:对字典的并发访问

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

边缘案例#2:不安全代码

如果不安全代码引发NullReferenceException,您可以查看指针变量,并检查它们是否存在IntPtr.Zero或其他内容。这是同一回事(“空指针异常”),但在不安全的代码中,变量通常被转换为值类型/数组等,你会把头撞在墙上,想知道值类型如何抛出此异常。

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

边缘案例3:Visual Studio多监视器设置,辅助监视器的DPI设置与主监视器不同

此边缘案例是特定于软件的,属于Visual Studio 2019 IDE(以及可能更早的版本)。

一种重现问题的方法:将任何组件从工具箱拖到非主监视器上的Windows窗体上,该窗体的DPI设置与主监视器不同,然后会弹出一个“Object reference not set to A instance of A Object”(对象引用未设置为对象的实例)。根据这个线程,这个问题已经知道了很长一段时间,在编写时仍然没有解决。