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

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

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


当前回答

请注意,无论情况如何,.NET中的原因总是相同的:

您正在尝试使用值为Nothing/null的引用变量。当引用变量的值为Nothing/null时,这意味着它实际上没有保存对堆上存在的任何对象实例的引用。您要么从未向变量赋值,要么从未创建分配给变量的值的实例,要么手动将变量设置为Nothing/null,要么为您调用了将变量设置成Nothing/nnull的函数。

其他回答

当实体框架中使用的实体的类名与web表单代码隐藏文件的类名相同时,添加一种情况。

假设您有一个web表单Contact.aspx,其代码尾类为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;}
}

和Contact实体类。有时实体类是分部类,因此您也可以在其他文件中扩展它们。

public partial class Contact 
{
    public string Name {get; set;}
}

当实体和代码尾类都在同一命名空间中时,会发生错误。要解决此问题,请重命名Contact.aspx的实体类或codebehind类。

原因我仍然不确定原因。但每当任何实体类将扩展System.Web.UI.Page时,都会发生此错误。

有关讨论,请查看DbContext.saveChanges()中的NullReferenceException

请注意,无论情况如何,.NET中的原因总是相同的:

您正在尝试使用值为Nothing/null的引用变量。当引用变量的值为Nothing/null时,这意味着它实际上没有保存对堆上存在的任何对象实例的引用。您要么从未向变量赋值,要么从未创建分配给变量的值的实例,要么手动将变量设置为Nothing/null,要么为您调用了将变量设置成Nothing/nnull的函数。

实际上,修复NullReferenceExeption的最简单方法有两种。

例如,如果你有一个附带脚本的GameObject和一个名为rb(rigidbody)的变量,那么当你开始游戏时,这个变量将以null开头。这就是为什么会得到NullReferenceExeption,因为计算机中没有存储在该变量中的数据。

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

使用AddComponent>Physics>RigidBody将刚体添加到对象然后进入脚本并键入rb=GetComponent<Rigidbody>();这行代码在Start()或Awake()函数下最有效。您可以通过编程方式添加一个组件,同时用一行代码分配变量:rb=AddComponent<RigidBody>();

进一步注意:如果您希望Unity向您的对象添加组件,而您可能忘记添加组件,那么可以在类声明上方键入[RequireComponent(typeof(RigidBody))](所有using下方的空格)。

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

你能怎么办?

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

检查参数

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

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

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

使用工具

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

在“Microsoft代码契约”中,您可以使用Contract.Requals(obj!=null)这样的语法,这为您提供了运行时和编译检查:引入代码契约。

还有“PostSharp”,它允许您只使用如下属性:

public void DoSometing([NotNull] obj)

通过这样做并使PostSharp成为构建过程的一部分,将在运行时检查obj是否为空。参见:PostSharp空检查

普通代码解决方案

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

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

您使用的方式与使用Nullable<T>的方式非常相似,但目的恰恰相反——不允许null。以下是一些示例:

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

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

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

正如您在上面看到的,对于空值,您可以通过value属性访问基础值。或者,您可以使用显式或隐式转换,您可以看到以下返回值的示例:

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

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

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

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

结合扩展

将NotNull<T>与扩展方法相结合,您可以涵盖更多情况。下面是扩展方法的示例:

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull<T>(this T @this) where T: class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

下面是一个如何使用它的示例:

var person = GetPerson().NotNull();

github

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

https://github.com/luisperezphd/NotNull

相关语言功能

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

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

var address = country?.State?.County?.City;

假设country是一个country类型的对象,该对象具有名为State等属性。如果country、State、County或City为空,则地址将变为空。因此,您只需检查地址是否正确。

这是一个很好的功能,但它提供的信息较少。这并不能明显看出4中的哪一个是空的。

像Nullable一样内置?

C#对Nullable<T>有一个很好的简写,你可以在类型后面加一个问号,比如so int?。

如果C#有类似于上面NotNull<T>结构的东西,并且有类似的速记,也许是感叹号(!),这样你就可以写类似于:public void WriteName(Person!Person)的东西了。

抛出此异常的一个示例是:当您试图检查某个对象时,该对象为空。

例如:

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
} 

当您尝试对尚未实例化的对象(即上面的代码)执行操作时,.NET运行时将抛出NullReferenceException。

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

更多信息请参见C#NullReferenceException和Null参数。