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

What is a NullReferenceException, and how do I fix it?

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

Object reference not set to an instance of an object.

这意味着什么?我该怎么做来修复这个错误?


原因是什么?底线

您试图使用的是null(或vb.net中的Nothing)。这意味着你要么把它设置为null,要么根本不设置为任何东西。好的。

像其他任何东西一样,null也会被传递出去。如果方法"a"中是null,则可能是方法"b"将null传递给方法"a"。好的。

null可以有不同的含义:好的。

  • 对象变量未初始化,因此不指向任何内容。在这种情况下,如果您访问这些对象的属性或方法,它将导致一个NullReferenceException
  • 开发人员有意使用null来表示没有任何有意义的价值。注意,C具有变量可空数据类型的概念(例如数据库表可以有可空字段),您可以将null分配给它们,以指示其中没有存储值,例如int? a = null;,其中问号表示允许将空值存储在变量a中。您可以使用if (a.HasValue) {...}if (a==null) {...}进行检查。可以为空的变量,比如a,这个例子允许通过a.Value显式地访问值,或者像通过a一样正常地访问值。
    注意,通过a.Value访问它会抛出一个InvalidOperationException而不是一个NullReferenceException,如果anull-您应该事先进行检查,即,如果您在可空变量int b;上有另一个,那么您应该执行类似if (a.HasValue) { b = a.Value; }或更短的if (a != null) { b = a; }的分配。
  • 本文的其余部分将更详细地介绍并显示许多程序员经常犯的错误,这些错误可能导致NullReferenceException。好的。更具体地说

    抛出NullReferenceException的运行时总是意味着相同的事情:您试图使用引用,但该引用没有初始化(或者它曾经初始化过,但现在不再初始化)。好的。

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

    1
    2
    string foo = null;
    foo.ToUpper();

    这将在第二行抛出一个NullReferenceException,因为不能对指向nullstring引用调用实例方法ToUpper()。好的。调试

    你如何找到NullReferenceException的来源?除了查看异常本身(将在异常发生的位置引发异常),在Visual Studio中调试的一般规则还适用:放置战略断点并检查变量,方法是将鼠标悬停在变量的名称上,打开(快速)监视窗口,或使用各种调试面板(如局部变量和乌托斯。好的。

    如果要查找引用的位置,请右键单击其名称并选择"查找所有引用"。然后可以在每个找到的位置放置一个断点,并在附加了调试器的情况下运行程序。每当调试器在这样一个断点上中断时,您需要确定您是否希望引用为非空,检查变量,并验证它是否在您期望的时候指向实例。好的。

    通过这样遵循程序流,您可以找到实例不应为空的位置以及未正确设置实例的原因。好的。实例

    可以引发异常的一些常见情况:好的。通用的

    1
    ref1.ref2.ref3.member

    如果ref1、ref2或ref3为空,则会得到一个NullReferenceException。如果要解决此问题,请通过将表达式重写为其更简单的等效表达式,找出哪个是空值:好的。

    1
    2
    3
    4
    var r1 = ref1;
    var r2 = r1.ref2;
    var r3 = r2.ref3;
    r3.member

    具体来说,在HttpContext.Current.User.Identity.Name中,HttpContext.Current可以为空,或者User属性可以为空,或者Identity属性可以为空。好的。间接的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Person {
        public int Age { get; set; }
    }
    public class Book {
        public Person Author { get; set; }
    }
    public class Example {
        public void Foo() {
            Book b1 = new Book();
            int authorAge = b1.Author.Age; // You never initialized the Author property.
                                           // there is no Person to get an Age from.
        }
    }

    如果要避免子(person)空引用,可以在父(book)对象的构造函数中对其进行初始化。好的。嵌套对象初始值设定项

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

    1
    Book b1 = new Book { Author = { Age = 45 } };

    这就意味着好的。

    1
    2
    Book b1 = new Book();
    b1.Author.Age = 45;

    虽然使用了new关键字,但它只创建了Book的新实例,而不是Person的新实例,所以Author属性仍然是null。好的。 嵌套集合初始化器

    1
    2
    3
    4
    5
    6
    public class Person {
        public ICollection<Book> Books { get; set; }
    }
    public class Book {
        public string Title { get; set; }
    }

    嵌套集合初始化器的表现一样。

    好。

    1
    2
    3
    4
    5
    6
    Person p1 = new Person {
        Books = {
            new Book { Title ="Title1" },
            new Book { Title ="Title2" },
        }
    };

    这两个translates

    好。

    1
    2
    3
    Person p1 = new Person();
    p1.Books.Add(new Book { Title ="Title1" });
    p1.Books.Add(new Book { Title ="Title2" });

    new Person只读Person审部创建的,但仍然nullBooks收藏。《集合初始化器语法不create a collection 对p1.Books,它只translates的p1.Books.Add(...)报表。

    好。 阵列

    1
    2
    int[] numbers = null;
    int n = numbers[0]; // numbers is null. There is no array to index.

    数组元素

    1
    2
    3
    Person[] people = new Person[5];
    people[0].Age = 20 // people[0] is null. The array was allocated but not
                       // initialized. There is no Person to set the Age for.

    jagged阵列

    1
    2
    3
    long[][] array = new long[1][];
    array[0][0] = 3; // is null because only the first dimension is yet initialized.
                     // Use array[0] = new long[2]; first.

    收集/战略/词典

    1
    2
    3
    Dictionary<string, int> agesForNames = null;
    int age = agesForNames["Bob"]; // agesForNames is null.
                                   // There is no Dictionary to perform the lookup.

    (范围可变间接/ deferred)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Person {
        public string Name { get; set; }
    }
    var people = new List<Person>();
    people.Add(null);
    var names = from p in people select p.Name;
    string firstName = names.First(); // Exception is thrown here, but actually occurs
                                      // on the line above. "p" is null because the
                                      // first element we added to the list is null.

    事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Demo
    {
        public event EventHandler StateChanged;

        protected virtual void OnStateChanged(EventArgs e)
        {        
            StateChanged(this, e); // Exception is thrown here
                                   // if no event handlers have been attached
                                   // to StateChanged event
        }
    }

    问:命名公约

    如果你从"字段differently locals,你可能永远不会知道,你的initialized场。

    好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Form1 {
        private Customer customer;

        private void Form1_Load(object sender, EventArgs e) {
            Customer customer = new Customer();
            customer.Name ="John";
        }

        private void Button_Click(object sender, EventArgs e) {
            MessageBox.Show(customer.Name);
        }
    }

    这可能是solved市以下两个领域的会议和underscore字头:

    好。

    1
    private Customer _customer;

    ASP.NET页生命周期:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public partial class Issues_Edit : System.Web.UI.Page
    {
        protected TestIssue myIssue;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                // Only called on first load, not when button clicked
                myIssue = new TestIssue();
            }
        }

        protected void SaveButton_Click(object sender, EventArgs e)
        {
            myIssue.Entry ="NullReferenceException here!";
        }
    }

    ASP.NET会话值

    1
    2
    3
    // if the"FirstName" session value has not yet been set,
    // then this line will throw a NullReferenceException
    string firstName = Session["FirstName"].ToString();

    ASP.NET MVC的View的空模型

    如果出现例外,当一部@Modelreferencing物业在一个ASP.NET MVC的View,你需要明白,我们Model看到在你的动作方法,当你return一景。当你返回的空模型(或模型的性质)从你的控制器,当出现例外观址信息:

    好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Controller
    public class Restaurant:Controller
    {
        public ActionResult Search()
        {
             return View();  // Forgot the provide a Model here.
        }
    }

    // Razor view
    @foreach (var restaurantSearch in Model.RestaurantSearch)  // Throws.
    {
    }

    <p>
    @Model.somePropertyName
    </p>Ok.

     <!-- Also throws -->

    wpf创造秩序和控制。

    wpf Controls是创造了在InitializeComponent叫二阶appear在他们的视觉树。这是一NullReferenceException育的早期,情况与事件创造了新的控制等,这是在这四个InitializeComponent参考控制上的创新。

    好。

    例如:

    好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <Grid>
        <!-- Combobox declared first -->
        <ComboBox Name="comboBox1"
                  Margin="10"
                  SelectedIndex="0"
                  SelectionChanged="comboBox1_SelectionChanged">
            <ComboBoxItem Content="Item 1" />
            <ComboBoxItem Content="Item 2" />
            <ComboBoxItem Content="Item 3" />
        </ComboBox>

        <!-- Label declared later -->
        <Label Name="label1"
               Content="Label"
               Margin="10" />
    </Grid>

    在这里创造了label1comboBox1冰。如果两个参考comboBox1_SelectionChangedattempts Label1",它将没有已经创造了。

    好。

    1
    2
    3
    4
    private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
    }

    变化的阶的XAML声明的(即,listing label1comboBox1之前,ignoring大学哲学问题的设计,将至少NullReferenceExceptionRESOLVE在这里。

    好。 铸造用as

    1
    var myThing = someObject as Thing;

    这不是把一个invalidcastexception归来null当铸fails(当它someobject冰空)。那婊子的感知。

    好。 firstordefault(LINQ)和singleordefault()

    《平原First()版本和Single()把exceptions当有冰淇淋什么的。"ordefault"版本的回报,在那空的案例。那婊子的感知。

    好。 foreach

    当你foreachthrows试图遍历零集。通常,导致城市unexpected null结果的方法,从馆藏的回报。

    好。

    1
    2
     List<int> list = null;    
     foreach(var v in list) { } // exception

    享受realistic实例——选择节点从XML文件。如果是会把节点的初始调试,但没有发现有效的节目,所有的属性:

    好。

    1
     foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))

    两种方式,避 没有检查和null忽略空值。

    如果你期望的参考,有时是空的,你可以检查网络访问null之前被审人员:

    好。

    1
    2
    3
    4
    5
    void PrintName(Person p) {
        if (p != null) {
            Console.WriteLine(p.Name);
        }
    }

    没有支票和一null提供默认值。

    你期望的两个方法调用返回的实例可以返回null,例如,当对象被思想不能被发现。你可以选择两个临界值的时候(这是缺省的案例:

    好。

    1
    2
    3
    4
    5
    string GetCategory(Book b) {
        if (b == null)
            return"Unknown";
        return b.Category;
    }

    没有检查方法和要求,从nullthrow a自定义例外。

    你可以把一个自定义例外也只抓住它的呼叫代码:

    好。

    1
    2
    3
    4
    5
    6
    string GetCategory(string bookTitle) {
        var book = library.FindBook(bookTitle);  // This may return null
        if (book == null)
            throw new BookNotFoundException(bookTitle);  // Your custom exception
        return book.Category;
    }

    如果一个使用Debug.Assert价值应该永远是null,两人之前抓住问题而出现的例外。

    当你知道在开发这一方法也许可以,但不应该null回报,你可以使用两个破发Debug.Assert()尽快occur:当它。

    好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    string GetTitle(int knownBookID) {
        // You know this should never return null.
        var book = library.GetBook(knownBookID);  

        // Exception will occur on the next line instead of at the end of this method.
        Debug.Assert(book != null,"Library didn't return a book for known book ID.");

        // Some other code

        return book.Title; // Will never throw NullReferenceException in Debug mode.
    }

    虽然这种检查不会在您的发布版本中结束,但会导致它在运行时处于发布模式时再次抛出NullReferenceException。好的。对于可以为空的值类型,使用GetValueOrDefault()提供默认值(当它们是null时)。

    1
    2
    3
    4
    5
    6
    7
    DateTime? appointment = null;
    Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
    // Will display the default value provided (DateTime.Now), because appointment is null.

    appointment = new DateTime(2022, 10, 20);
    Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
    // Will display the appointment date, not the default

    使用空合并运算符:??If()vb。

    当遇到null时提供默认值的简写:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    IService CreateService(ILogger log, Int32? frobPowerLevel)
    {
        var serviceImpl = new MyService(log ?? NullLog.Instance);

        // Note that the above"GetValueOrDefault()" can also be rewritten to use
        // the coalesce operator:
        serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
    }

    对数组使用空条件运算符:?.?[x](在c 6和vb.net 14中提供):

    这有时也被称为安全导航或ELVIS(在其形状之后)操作员。如果运算符左侧的表达式为空,则不会计算右侧的表达式,而是返回空值。这意味着像这样的情况:好的。

    1
    var title = person.Title.ToUpper();

    如果此人没有标题,这将引发异常,因为它试图对值为空的属性调用ToUpper。好的。

    在C 5及以下,可通过以下方式进行防护:好的。

    1
    var title = person.Title == null ? null : person.Title.ToUpper();

    现在,标题变量将为空,而不是引发异常。C 6为此引入了较短的语法:好的。

    1
    var title = person.Title?.ToUpper();

    这将导致标题变量为null,如果person.Titlenull,则不会调用ToUpper。好的。

    当然,您仍然需要检查title是否为空,或者使用空条件运算符和空合并运算符(??来提供默认值:好的。

    1
    2
    3
    4
    5
    6
    7
    // regular null check
    int titleLength = 0;
    if (title != null)
        titleLength = title.Length; // If title is null, this would throw NullReferenceException

    // combining the `?` and the `??` operator
    int titleLength = title?.Length ?? 0;

    同样,对于阵列,可以使用?[i]如下:好的。

    1
    2
    3
    4
    int[] myIntArray=null;
    var i=5;
    int? elem = myIntArray?[i];
    if (!elem.HasValue) Console.WriteLine("No value");

    这将执行以下操作:如果myIntaray为空,则表达式返回空值,您可以安全地检查它。如果它包含一个数组,它将执行以下操作:elem = myIntArray[i];并返回ith元素。好的。在迭代器中调试和修复空解列器的特殊技术

    C支持"迭代器块"(在其他一些流行语言中称为"生成器")。由于延迟执行,在迭代器块中调试空解引用异常可能特别困难:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
    {
        for (int i = 0; i < count; ++i)
          yield return f.MakeFrob();
    }
    ...
    FrobFactory factory = whatever;
    IEnumerable<Frobs> frobs = GetFrobs();
    ...
    foreach(Frob frob in frobs) { ... }

    如果whatever导致null,那么MakeFrob将抛出。现在,你可能认为正确的做法是:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    // DON'T DO THIS
    public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
    {
        if (f == null)
          throw new ArgumentNullException("f","factory must not be null");
        for (int i = 0; i < count; ++i)
          yield return f.MakeFrob();
    }

    为什么这是错误的?因为迭代器块在foreach之前不会实际运行!对GetFrobs的调用只返回一个对象,当迭代时,该对象将运行迭代器块。好的。

    通过这样编写一个空检查,您可以防止空引用,但是您将空参数异常移动到迭代点,而不是调用点,这对于调试来说非常混乱。好的。

    正确的解决方法是:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // DO THIS
    public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
    {
        // No yields in a public method that throws!
        if (f == null)
          throw new ArgumentNullException("f","factory must not be null");
        return GetFrobsForReal(f, count);
    }
    private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
    {
        // Yields in a private method
        Debug.Assert(f != null);
        for (int i = 0; i < count; ++i)
          yield return f.MakeFrob();
    }

    也就是说,生成具有迭代器块逻辑的私有helper方法,以及执行空检查并返回迭代器的公共surface方法。现在,当调用GetFrobs时,立即进行空检查,然后在迭代序列时执行GetFrobsForReal。好的。

    如果您检查linq-to对象的引用源,您将看到在整个过程中都使用了这种技术。写起来有点笨拙,但它使调试无效错误变得容易得多。优化代码是为了方便调用者,而不是为了方便作者。好的。关于不安全代码中空解引用的注释

    C有一个"不安全"模式,顾名思义,这是非常危险的,因为提供内存安全和类型安全的正常安全机制没有被强制执行。你不应该写不安全的代码,除非你对内存的工作方式有一个透彻和深入的理解。好的。

    在不安全模式下,您应该了解两个重要事实:好的。

    • 取消对空指针的引用会产生与取消对空引用的引用相同的异常。
    • 取消对无效非空指针的引用会产生该异常在某些情况下

    为了理解这是为什么,首先理解.NET是如何产生空的取消引用异常的。(这些详细信息适用于在Windows上运行的.NET;其他操作系统使用类似的机制。)好的。

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

    C中的空指针和空引用在内部都表示为数字零,因此任何试图将其取消引用到相应内存存储中的操作系统都会产生错误。然后.NET运行时检测到该错误并将其转换为空的取消引用异常。好的。

    这就是取消对空指针和空引用的引用会产生相同异常的原因。好的。

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

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

    如果您正在处理不安全的代码,并且您得到一个空的取消引用异常,请注意,有问题的指针不必为空。它可以是最低页面中的任何位置,并且将生成此异常。好的。好啊。


    空引用异常-Visual Basic

    VisualBasic的NullReference Exception与C中的没有区别。毕竟,它们都报告在.NET框架中定义的相同异常,它们都使用该异常。VisualBasic特有的原因很少(可能只有一个)。好的。

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

    注:好的。

  • 这是基于概念的:没有代码可以粘贴到项目中。它旨在帮助您了解是什么导致了NullReferenceException(NRE),如何找到它,如何修复它,以及如何避免它。NRE可以通过多种方式引起,因此这不太可能是您唯一的遭遇。
  • 示例(来自堆栈溢出日志)并不总是首先显示做某事的最佳方法。
  • 通常使用最简单的补救方法。
  • 基本含义

    消息"object not set to an instance of object"(对象未设置为对象的实例)表示您正在尝试使用尚未初始化的对象。这可以归结为:好的。

    • 您的代码声明了一个对象变量,但它没有初始化它(创建一个实例或"实例化"它)。
    • 您的代码假定某个对象会初始化,但没有
    • 可能,其他代码过早地使仍在使用的对象无效

    找到原因

    由于问题是一个对象引用,即Nothing,所以答案是检查它们以找出哪一个。然后确定未初始化的原因。将鼠标悬停在各种变量上,Visual Studio(vs)将显示它们的值-罪魁祸首将是Nothing。好的。

    IDE debug display好的。

    您还应该从相关代码中删除任何try/catch块,尤其是在catch块中没有任何内容的代码中。这将导致代码在试图使用Nothing对象时崩溃。这是您想要的,因为它将识别问题的确切位置,并允许您识别导致问题的对象。好的。

    在显示Error while...的捕获物中,一个MsgBox将没有什么帮助。此方法还会导致非常糟糕的堆栈溢出问题,因为您无法描述实际的异常、涉及的对象,甚至无法描述发生异常的代码行。好的。

    您还可以使用Locals Window(debug->windows->locals)检查对象。好的。

    一旦你知道问题是什么和在哪里,解决问题通常相当容易,而且比发布一个新问题要快。好的。

    参见:好的。

    • 断点
    • msdn:如何:使用try/catch块捕获异常
    • msdn:例外情况的最佳实践

    示例和补救措施类对象/创建实例

    1
    2
    3
    Dim reg As CashRegister
    ...
    TextBox1.Text = reg.Amount         ' NRE

    问题是,Dim不创建收银机对象;它只声明该类型的名为reg的变量。声明一个对象变量和创建一个实例是两件不同的事情。好的。

    补救方法好的。

    当您声明实例时,通常可以使用New运算符来创建该实例:好的。

    1
    2
    3
    4
    Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

    '
    Longer, more explicit form:
    Dim reg As CashRegister = New CashRegister

    如果以后只适合创建实例:好的。

    1
    2
    3
    Private reg As CashRegister         ' Declare
      ...
    reg = New CashRegister()            '
    Create instance

    注意:不要在一个过程中再次使用Dim,包括构造函数(Sub New):好的。

    1
    2
    3
    4
    5
    6
    7
    Private reg As CashRegister
    '...

    Public Sub New()
       '
    ...
       Dim reg As New CashRegister
    End Sub

    这将创建一个局部变量,reg,它只存在于该上下文中(sub)。模块级别为Scopereg变量将在其他任何地方使用,但它仍然是Nothing。好的。< Buff行情>

    缺少New运算符是导致在所审查的堆栈溢出问题中出现NullReference Exceptions的原因之一。好的。

    VisualBasic尝试使用New反复地使过程清晰化:使用New运算符创建一个新对象并调用Sub New构造函数,对象可以在其中执行任何其他初始化。好的。< /块引用>

    很明显,DimPrivate只声明一个变量及其Type。变量的范围(无论它是存在于整个模块/类中还是在过程的本地)由声明它的位置决定。Private | Friend | Public定义了访问级别,而不是范围。好的。

    有关详细信息,请参阅:好的。

    • 新算子
    • Visual Basic中的范围
    • Visual Basic中的访问级别
    • 值类型和引用类型

    数组

    数组也必须实例化:好的。

    1
    Private arr as String()

    此数组仅已声明,未创建。初始化数组有几种方法:好的。

    1
    2
    3
    4
    5
    6
    Private arr as String() = New String(10){}
    ' or
    Private arr() As String = New String(10){}

    '
    For a local array (in a procedure) and using 'Option Infer':
    Dim arr = New String(10) {}

    注:从vs 2010开始,当使用literal和Option Infer初始化本地数组时,As New元素是可选的:好的。

    1
    2
    3
    Dim myDbl As Double() = {1.5, 2, 9.9, 18, 3.14}
    Dim myDbl = New Double() {1.5, 2, 9.9, 18, 3.14}
    Dim myDbl() = {1.5, 2, 9.9, 18, 3.14}

    数据类型和数组大小是根据所分配的数据推断出来的。类/模块级声明仍然需要使用Option StrictAs :好的。

    1
    Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}

    示例:类对象数组好的。

    1
    2
    3
    4
    5
    Dim arrFoo(5) As Foo

    For i As Integer = 0 To arrFoo.Count - 1
       arrFoo(i).Bar = i * 10       ' Exception
    Next

    数组已创建,但其中的Foo对象尚未创建。好的。

    补救方法好的。

    1
    2
    3
    4
    For i As Integer = 0 To arrFoo.Count - 1
        arrFoo(i) = New Foo()         ' Create Foo instance
        arrFoo(i).Bar = i * 10
    Next

    使用List(Of T)将使没有有效对象的元素变得非常困难:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    Dim FooList As New List(Of Foo)     ' List created, but it is empty
    Dim f As Foo                        '
    Temporary variable for the loop

    For i As Integer = 0 To 5
        f = New Foo()                    ' Foo instance created
        f.Bar =  i * 10
        FooList.Add(f)                   '
    Foo object added to list
    Next

    有关详细信息,请参阅:好的。

    • 期权推断声明
    • Visual Basic中的范围
    • Visual Basic中的数组

    列表和集合

    .NET集合(其中有许多变体-列表、字典等)也必须实例化或创建。好的。

    1
    2
    3
    Private myList As List(Of String)
    ..
    myList.Add("ziggy")           ' NullReference

    由于同样的原因,您会得到相同的异常——只声明了myList,但没有创建实例。补救方法相同:好的。

    1
    2
    3
    4
    myList = New List(Of String)

    ' Or create an instance when declared:
    Private myList As New List(Of String)

    共同监督是使用集合Type的类:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Public Class Foo
        Private barList As List(Of Bar)

        Friend Function BarCount As Integer
            Return barList.Count
        End Function

        Friend Sub AddItem(newBar As Bar)
            If barList.Contains(newBar) = False Then
                barList.Add(newBar)
            End If
        End Function

    任何一个过程都将导致nre,因为barList只是声明的,而不是实例化的。创建Foo的实例也不会创建内部barList的实例。可能是为了在构造函数中这样做:好的。

    1
    2
    3
    4
    Public Sub New         ' Constructor
        '
    Stuff to do when a new Foo is created...
        barList = New List(Of Bar)
    End Sub

    和以前一样,这是不正确的:好的。

    1
    2
    3
    4
    Public Sub New()
        ' Creates another barList local to this procedure
         Dim barList As New List(Of Bar)
    End Sub

    有关更多信息,请参见List(Of T)类。好的。数据提供程序对象

    使用数据库提供了许多空引用的机会,因为可以同时使用许多对象(CommandConnectionTransactionDatasetDataTableDataRows…)。注意:无论您使用的是哪种数据提供程序——MySQL、SQL Server、OLEDB等——这些概念都是相同的。好的。

    例1好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Dim da As OleDbDataAdapter
    Dim ds As DataSet
    Dim MaxRows As Integer

    con.Open()
    Dim sql ="SELECT * FROM tblfoobar_List"
    da = New OleDbDataAdapter(sql, con)
    da.Fill(ds,"foobar")
    con.Close()

    MaxRows = ds.Tables("foobar").Rows.Count      ' Error

    和以前一样,声明了ds数据集对象,但从未创建实例。DataAdapter将填充现有的Dataset而不是创建一个。在这种情况下,由于ds是一个局部变量,因此IDE警告您可能会发生这种情况:好的。

    img好的。

    当声明为模块/类级变量时,如con所示,编译器不知道对象是否由上游过程创建。不要忽略警告。好的。

    补救方法好的。

    1
    Dim ds As New DataSet

    例2好的。

    1
    2
    3
    4
    5
    6
    ds = New DataSet
    da = New OleDBDataAdapter(sql, con)
    da.Fill(ds,"Employees")

    txtID.Text = ds.Tables("Employee").Rows(0).Item(1)
    txtID.Name = ds.Tables("Employee").Rows(0).Item(2)

    打字错误是一个问题:EmployeesEmployee。没有创建名为"雇员"的DataTable,因此NullReferenceException的结果试图访问它。另一个潜在的问题是假设存在Items,当SQL包含一个WHERE子句时,这可能不是这样。好的。

    补救方法好的。

    因为它使用一个表,所以使用Tables(0)可以避免拼写错误。检查Rows.Count也有助于:好的。

    1
    2
    3
    4
    If ds.Tables(0).Rows.Count > 0 Then
        txtID.Text = ds.Tables(0).Rows(0).Item(1)
        txtID.Name = ds.Tables(0).Rows(0).Item(2)
    End If

    Fill是返回受影响的Rows数量的函数,也可以测试:好的。

    1
    If da.Fill(ds,"Employees") > 0 Then...

    例3好的。

    1
    2
    3
    4
    5
    6
    7
    Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,
            TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOIN
            FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ..."
    , con)
    Dim ds As New DataSet
    da.Fill(ds)

    If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then

    DataAdapter将提供TableNames,如前一个示例所示,但它不解析SQL或数据库表中的名称。因此,ds.Tables("TICKET_RESERVATION")引用了一个不存在的表。好的。

    补救方法相同,按索引参考表:好的。

    1
    If ds.Tables(0).Rows.Count > 0 Then

    另请参见DataTable类。好的。对象路径/嵌套

    1
    2
    If myFoo.Bar.Items IsNot Nothing Then
       ...

    代码只测试Items,而myFooBar也可能是空的。补救方法是测试对象的整个链或路径,一次一个:好的。

    1
    2
    3
    4
    If (myFoo IsNot Nothing) AndAlso
        (myFoo.Bar IsNot Nothing) AndAlso
        (myFoo.Bar.Items IsNot Nothing) Then
        ....

    AndAlso很重要。一旦遇到第一个False条件,将不会执行后续测试。这允许代码一次安全地"钻入"对象一个"级别",只有在(并且如果)确定myFoo有效之后才对myFoo.Bar进行评估。对复杂对象进行编码时,对象链或路径可能会很长:好的。

    1
    myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")

    不可能引用null对象的任何"下游"对象。这也适用于控制:好的。

    1
    myWebBrowser.Document.GetElementById("formfld1").InnerText ="some value"

    在这里,myWebBrowserDocument可能是空的,或者formfld1元素可能不存在。好的。用户界面控件

    1
    2
    3
    4
    5
    6
    Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar" _
         &"FROM Invoice where invoice_no = '" & _
         Me.ComboBox5.SelectedItem.ToString.Trim &"' And category = '" & _
         Me.ListBox1.SelectedItem.ToString.Trim &"' And item_name = '" & _
         Me.ComboBox2.SelectedValue.ToString.Trim &"' And expiry_date = '" & _
         Me.expiry.Text &"'", con)

    除其他之外,此代码并不期望用户可能没有在一个或多个UI控件中选择某些内容。ListBox1.SelectedItem很可能是Nothing,因此ListBox1.SelectedItem.ToString将导致nre。好的。

    补救方法好的。

    在使用之前验证数据(也使用Option Strict和sql参数):好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Dim expiry As DateTime         ' for text date validation
    If (ComboBox5.SelectedItems.Count > 0) AndAlso
        (ListBox1.SelectedItems.Count > 0) AndAlso
        (ComboBox2.SelectedItems.Count > 0) AndAlso
        (DateTime.TryParse(expiry.Text, expiry) Then

        '
    ... do stuff
    Else
        MessageBox.Show(...error message...)
    End If

    或者,您可以使用(ComboBox5.SelectedItem IsNot Nothing) AndAlso...。好的。Visual Basic窗体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Public Class Form1

        Private NameBoxes = New TextBox(5) {Controls("TextBox1"), _
                       Controls("TextBox2"), Controls("TextBox3"), _
                       Controls("TextBox4"), Controls("TextBox5"), _
                       Controls("TextBox6")}

        ' same thing in a different format:
        Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}

        '
    Immediate NRE:
        Private somevar As String = Me.Controls("TextBox1").Text

    这是获得NRE的一种相当常见的方法。在C中,根据编码方式,IDE将报告当前上下文中不存在Controls,或者"不能引用非静态成员"。所以,在某种程度上,这只是一个vb环境。它也很复杂,因为它会导致故障级联。好的。

    无法以这种方式初始化数组和集合。此初始化代码将在构造函数创建FormControls之前运行。因此:好的。

    • 列表和集合将只是空的
    • 数组将包含五个零元素
    • somevar转让将导致立即的NRE,因为没有任何东西不具有.Text属性。

    稍后引用数组元素将导致nre。如果您在Form_Load中这样做,由于一个奇怪的错误,IDE可能不会在异常发生时报告它。当代码尝试使用数组时,异常将在稍后弹出。这个"沉默例外"在本文中有详细介绍。就我们的目的而言,关键在于,当在创建表单时发生灾难性事件(Sub NewForm Load事件),异常可能无法报告,代码退出过程并只显示表单。好的。

    由于您的Sub NewForm Load事件中的任何其他代码都不会在nre之后运行,因此许多其他东西都可以不初始化。好的。

    1
    2
    3
    4
    5
    6
    7
    Sub Form_Load(..._
       '...
       Dim name As String = NameBoxes(2).Text        '
    NRE
       ' ...
       '
    More code (which will likely not be executed)
       ' ...
    End Sub

    注:这适用于任何和所有控制和组件引用,使得这些引用在以下情况下是非法的:好的。

    1
    2
    3
    4
    5
    Public Class Form1

        Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
        Private dbcon As String = OpenFileDialog1.FileName &";Jet Oledb..."
        Private studentName As String = TextBox13.Text

    部分补救好的。

    奇怪的是,vb没有提供警告,但是补救方法是在表单级别声明容器,但是当控件确实存在时,在表单加载事件处理程序中初始化它们。这可以在Sub New中完成,只要您的代码在InitializeComponent调用之后:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ' Module level declaration
    Private NameBoxes as TextBox()
    Private studentName As String

    '
    Form Load, Form Shown or Sub New:
    '
    '
    Using the OP's approach (illegal using OPTION STRICT)
    NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
    studentName = TextBox32.Text           '
    For simple control references

    数组代码可能还没有脱离危险。在容器控件(如GroupBoxPanel中)中的任何控件都不会在Me.Controls中找到;它们将在该面板或分组框的控件集合中。当控件名称拼写错误时,也不会返回控件("TeStBox2")。在这种情况下,Nothing将再次存储在这些数组元素中,当您试图引用它时,将产生一个nre。好的。

    既然你知道自己在寻找什么,就应该很容易找到:VS shows you the error of your ways好的。

    "button2"位于Panel上。好的。

    补救方法好的。

    不要使用表单的Controls集合以名称间接引用,而是使用控制引用:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ' Declaration
    Private NameBoxes As TextBox()

    '
    Initialization -  simple and easy to read, hard to botch:
    NameBoxes = New TextBox() {TextBox1, TextBox2, ...)

    ' Initialize a List
    NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
    '
    or
    NamesList = New List(Of TextBox)
    NamesList.AddRange({TextBox1, TextBox2, TextBox3...})

    函数不返回任何内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Private bars As New List(Of Bars)        ' Declared and created

    Public Function BarList() As List(Of Bars)
        bars.Clear
        If someCondition Then
            For n As Integer = 0 to someValue
                bars.Add(GetBar(n))
            Next n
        Else
            Exit Function
        End If

        Return bars
    End Function

    在这种情况下,IDE将警告您"并非所有路径都返回值,可能会导致NullReferenceException"。你可以通过用Return Nothing替换Exit Function来抑制警告,但这并不能解决问题。任何试图在someCondition = False时使用回报的行为都将导致NRE:好的。

    1
    2
    3
    bList = myFoo.BarList()
    For Each b As Bar in bList      ' EXCEPTION
          ...

    补救方法好的。

    将函数中的Exit Function替换为Return bList。返回空的List与返回的Nothing不同。如果返回的对象有可能是Nothing,请在使用前进行测试:好的。

    1
    2
     bList = myFoo.BarList()
     If bList IsNot Nothing Then...

    执行不当的尝试/捕获

    实施不当的Try/Catch可以隐藏问题所在并导致新问题出现:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    Dim dr As SqlDataReader
    Try
        Dim lnk As LinkButton = TryCast(sender, LinkButton)
        Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
        Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
        ViewState("username") = eid
        sqlQry ="select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
                 Pager, mailaddress, from employees1 where username='"
    & eid &"'"
        If connection.State <> ConnectionState.Open Then
            connection.Open()
        End If
        command = New SqlCommand(sqlQry, connection)

        'More code fooing and barring

        dr = command.ExecuteReader()
        If dr.Read() Then
            lblFirstName.Text = Convert.ToString(dr("FirstName"))
            ...
        End If
        mpe.Show()
    Catch

    Finally
        command.Dispose()
        dr.Close()             '
    <-- NRE
        connection.Close()
    End Try

    这是一个对象没有按预期创建的情况,但也显示了空Catch的反作用。好的。

    SQL中有一个额外的逗号(在"mailaddress"之后),这会导致.ExecuteReader处出现异常。在Catch不做任何事情后,Finally试图进行清理,但由于你不能将Close作为一个无效的DataReader对象,一个全新的NullReferenceException结果。好的。

    一个空的Catch街区是魔鬼的游乐场。这一行动让人困惑,为什么他在Finally区得到了一个NRE。在其他情况下,空的Catch可能会导致下游更严重的问题,并导致你花时间在错误的地方寻找错误的问题。(上述"无声例外"提供了相同的娱乐价值。)好的。

    补救方法好的。

    不要使用空的Try/Catch块-让代码崩溃,这样您就可以a)确定原因b)确定位置c)应用适当的补救措施。Try/Catch块并不是为了向唯一有资格修复异常的人(开发人员)隐藏异常。好的。dbnull与Nothing不同

    1
    2
    3
    For Each row As DataGridViewRow In dgvPlanning.Rows
        If Not IsDBNull(row.Cells(0).Value) Then
            ...

    IsDBNull函数用于测试msdn中的值是否等于System.DBNull:好的。

    The System.DBNull value indicates that the Object represents missing or non-existent data. DBNull is not the same as Nothing, which indicates that a variable has not yet been initialized.

    Ok.

    补救方法好的。

    1
    If row.Cells(0) IsNot Nothing Then ...

    如前所述,您可以不测试任何内容,然后测试特定值:好的。

    1
    If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then

    例2好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Dim getFoo = (From f In dbContext.FooBars
                   Where f.something = something
                   Select f).FirstOrDefault

    If Not IsDBNull(getFoo) Then
        If IsDBNull(getFoo.user_id) Then
            txtFirst.Text = getFoo.first_name
        Else
           ...

    FirstOrDefault返回第一个项目或默认值,参考类型为Nothing,不返回DBNull:好的。

    1
    If getFoo IsNot Nothing Then...

    控制

    1
    2
    3
    4
    5
    6
    Dim chk As CheckBox

    chk = CType(Me.Controls(chkName), CheckBox)
    If chk.Checked Then
        Return chk
    End If

    如果找不到带有chkNameCheckBox(或存在于GroupBox中),那么chk将为空,试图引用任何财产将导致例外。好的。

    补救方法好的。

    1
    If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...

    数据报视图

    DGV有一些周期性的怪癖:好的。

    1
    2
    3
    4
    5
    dgvBooks.DataSource = loan.Books
    dgvBooks.Columns("ISBN").Visible = True       ' NullReferenceException
    dgvBooks.Columns("Title").DefaultCellStyle.Format ="C"
    dgvBooks.Columns("Author").DefaultCellStyle.Format ="C"
    dgvBooks.Columns("Price").DefaultCellStyle.Format ="C"

    如果dgvBooksAutoGenerateColumns = True的话,它将创建列,但不命名它们,所以当上面的代码按名称引用它们时失败。好的。

    补救方法好的。

    手动命名列,或按索引引用:好的。

    1
    dgvBooks.Columns(0).Visible = True

    例2-小心纽罗

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    xlWorkSheet = xlWorkBook.Sheets("sheet1")

    For i = 0 To myDGV.RowCount - 1
        For j = 0 To myDGV.ColumnCount - 1
            For k As Integer = 1 To myDGV.Columns.Count
                xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
                xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
            Next
        Next
    Next

    当EDOCX1 21的EDCOX1,22,EDCOX1,23,(默认),在底部的空白/新行中的EDCOX1×24,都包含EDCOX1×13。大多数使用内容的尝试(例如,ToString)都会导致NRE。好的。

    补救方法好的。

    使用For/Each循环并测试IsNewRow属性以确定它是否是最后一行。无论AllowUserToAddRows是否正确,这都是有效的:好的。

    1
    2
    3
    For Each r As DataGridViewRow in myDGV.Rows
        If r.IsNewRow = False Then
             ' ok to use this row

    如果确实使用了For n循环,请修改行计数,或者在IsNewRow为真时使用Exit For。好的。我的.settings(StringCollection)

    在某些情况下,尝试使用My.Settings中的一个项目(即StringCollection)可能会在第一次使用时导致空引用。解决方案是相同的,但并不明显。考虑:好的。

    1
    My.Settings.FooBars.Add("ziggy")         ' foobars is a string collection

    因为vb正在为您管理设置,所以期望它初始化集合是合理的。它将,但仅当您以前(在设置编辑器中)向集合中添加了初始条目时。由于集合在添加项时(显然)已初始化,因此当设置编辑器中没有要添加的项时,它将保持Nothing。好的。

    补救方法好的。

    如果需要,初始化窗体的Load事件处理程序中的设置集合:好的。

    1
    2
    3
    If My.Settings.FooBars Is Nothing Then
        My.Settings.FooBars = New System.Collections.Specialized.StringCollection
    End If

    通常,只需要在应用程序第一次运行时初始化Settings集合。另一种补救方法是在project->settings foobars中为集合添加初始值,保存项目,然后删除假值。好的。关键点

    你可能忘记了New接线员。好的。

    或好的。

    您认为可以完美地执行某些操作以将已初始化的对象返回到代码中,但事实并非如此。好的。

    不要忽略编译器警告(永远)并使用Option Strict On(总是)。好的。

    msdn nullreference异常好的。好啊。


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

    1
    2
    object o = null;
    DateTime d = (DateTime)o;

    它会把一个NullReferenceException扔到铸件上。在上面的示例中,这似乎很明显,但在更"后期绑定"的复杂场景中,可能会发生这种情况,其中空对象是从您不拥有的代码中返回的,而强制转换是由某些自动系统生成的。

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

    1
    " />

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


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

    1
    2
    SqlConnection connection = null;
    connection.Open();

    这将抛出错误,因为虽然我声明了变量"connection",但它没有指向任何东西。当我试图调用成员"EDOCX1"〔1〕时,没有可供它解决的引用,它会抛出错误。

    要避免此错误:

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


    这意味着您的代码使用了一个设置为空的对象引用变量(即它没有引用实际的对象实例)。

    为了防止出现错误,应该在使用之前测试可能为空的对象是否为空。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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
    }

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

    You are trying to use a reference variable whose value is Nothing/null. When the value is Nothing/null for the reference variable, that means it is not actually holding a reference to an instance of any object that exists on the heap.

    You either never assigned something to the variable, never created an instance of the value assigned to the variable, or you set the variable equal to Nothing/null manually, or you called a function that set the variable to Nothing/null for you.


    引发这个异常的一个例子是:当您试图检查某个东西时,它是空的。

    例如:

    1
    2
    3
    4
    5
    6
    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相比,如果一个方法期望传递给它的内容不是空的,则通常将其作为防御措施抛出。

    更多信息位于C NullReferenceException和空参数中。


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

    例子:

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

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

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

    为了完全理解引发NullReferenceException的原因,了解值类型和引用类型之间的区别是很重要的。

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

    只有引用类型(顾名思义)可以保存引用或直接指向任何对象(或"空")。而值类型总是包含一个值。

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

    • 动态
    • 对象
    • 一串

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

    • 数值类型
    • 积分类型
    • 浮点类型
    • 十进制的
    • 布尔
    • 用户定义的结构


    另一个可能发生NullReferenceExceptions的情况是(不正确)使用as运算符:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

    这里,BookCar是不兼容的类型;Car不能转换/转换为Book。当此强制转换失败时,as返回null。在这之后使用mybook会导致NullReferenceException

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

    如果您希望类型转换始终成功(即您知道对象应该提前完成),那么应该使用强制转换:

    1
    ComicBook cb = (ComicBook)specificBook;

    如果您不确定类型,但希望尝试将其用作特定类型,则使用as

    1
    2
    3
    4
    ComicBook cb = specificBook as ComicBook;
    if (cb != null) {
       // ...
    }


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

    例子:

    1
    2
    3
    4
    5
    string value = null;
    if (value.Length == 0) // <-- Causes exception
    {
        Console.WriteLine(value); // <-- Never reached
    }

    异常错误为:

    Unhandled Exception:

    System.NullReferenceException: Object reference not set to an instance
    of an object. at Program.Main()


    While what causes a NullReferenceExceptions and approaches to avoid/fix such an exception have been addressed in other answers, what many programmers haven't learned yet is how to independently debug such exceptions during development.

    In Visual Studio this is usually easy thanks to the Visual Studio Debugger.

    First, make sure that the correct error is going to be caught - see
    How do I allow breaking on 'System.NullReferenceException' in VS2010? Note1

    Then either Start with Debugging (F5) or Attach [the VS Debugger] to Running Process. On occasion it may be useful to use Debugger.Break, which will prompt to launch the debugger.

    Now, when the NullReferenceException is thrown (or unhandled) the debugger will stop (remember the rule set above?) on the line on which the exception occurred. Sometimes the error will be easy to spot.

    For instance,
    in the following line the only code that can cause the exception is if myString evaluates to null. This can be verified by looking at the Watch Window or running expressions in the Immediate Window.

    1
    var x = myString.Trim();

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

    1
    var x = str1.Trim() + str2.Trim();

    一旦找到异常被抛出的位置,通常向后推理以找出引入的空值[不正确]的位置是很简单的。--

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

    1如果中断引发的攻击性太强,并且调试器在.NET或第三方库中的NPE上停止,则可以使用未处理的中断用户来限制捕获的异常。此外,VS2012只引入了我的代码,我建议启用它。

    If you are debugging with Just My Code enabled, the behavior is slightly different. With Just My Code enabled, the debugger ignores first-chance common language runtime (CLR) exceptions that are thrown outside of My Code and do not pass through My Code


    西蒙:mourier给这个exampleP></

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

    在拆箱转换从安(铸造)(or from one of the objectSystem.ValueTypeSystem.Enum类或接口,或从安型(型)的值比在其他Nullable<>the NullReferenceException给本身)。P></

    在the other direction,节礼Nullable<>which has HasValue转换从平等到false参考型的,可以让我以后可以参考nullwhich to a NullReferenceException铅。茶经example is:P></

    1
    2
    3
    DateTime? d = null;
    var s = d.ToString();  // OK, no exception (no boxing), returns""
    var t = d.GetType();   // Bang! d is boxed, NullReferenceException

    有时发生在一节礼茶的方式。for example with this GENERIC:非扩展方法P></

    1
    2
    3
    4
    public static void MyExtension(this object x)
    {
      x.ToString();
    }

    problematic will be the following队列:P></

    1
    2
    DateTime? d = null;
    d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.

    这些案例arise because of the special rules when the uses拳击Nullable<>情况下运行。P></


    当实体框架中使用的实体的类名与Web表单代码隐藏文件的类名相同时,添加一个事例。

    假设您有一个web表单contact.aspx,其代码隐藏类是contact,并且您有一个实体名contact。

    然后在调用Context.SaveChanges()时,以下代码将引发NullReferenceException。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Contact contact = new Contact { Name ="Abhinav
    <hr><P>另一个可能收到这个异常的一般情况是在单元测试期间模拟类。无论使用的是模拟框架,都必须确保类层次结构的所有适当级别都被正确模拟。尤其是,被测试代码引用的<wyn>HttpContext</wyn>的所有属性都必须被模拟。</P><P>请参阅"
    测试自定义authorizationalattribute时引发的nullreferenceexception",以获取一些详细的示例。</P><p><center>[wp_ad_camp_3]</center></p><hr><P>我在不同的角度answering to this。this sort of answers"什么人能把avoid恩?"P></<P>当工作在不同的层中,for example安MVC应用到呼叫控制器服务,在业务运营的需要。在这样的场景依赖注入容器can be used to the to the初始化服务avoid NullReferenceException。我知道你不need to that means for零只是担心检查服务和呼叫控制器as the from the available to(虽然他们将永远与initialized)在AS或prototype或者单身。P></[cc lang="csharp"]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();
        }
    }


    of on the物"。要给什么,there can be many"的答案。P></

    更"正规"的方式这样的条件而开发preventing of error is求职模式的合同设计在你的尾巴。You need to this均值集类的不变量,和/或偶函数/方法preconditions在线你和后置条件系统,同时开发。P></

    总之,类不变量约束确保that there will be some that will not get在你的类中使用violated(正规和therefore Will not get the class,在安inconsistent了)。preconditions given as to that日期输入均值函数必须在后续的一些约束集法/永远不侵犯他们,和后置条件均值函数/方法,后续输出必备茶具又没有过约束violating them。合同的条件不好violated should of a无缺陷在执行程序,therefore设计模式在实践中checked contract is disabled在调试模式,而被释放,maximize to the自主开发的系统的性能。P></

    这样,你可以avoid NullReferenceExceptionthat are cases of the results of告警约束集。如果你使用for example,安布尔对象中一级物业X试图invoke one of its空值的方法和Xhas to this will then,NullReferenceException:铅P></

    1
    2
    3
    4
    5
    6
    public X { get; set; }

    public void InvokeX()
    {
        X.DoSomething(); // if X value is null, you will get a NullReferenceException
    }

    "但如果你不必须有属性集X为null值,那么"As You can防止precondition method,the before情景描述的程序办理:P></

    1
    2
    3
    4
    5
    6
    7
    //Using code contracts:
    [ContractInvariantMethod]
    protected void ObjectInvariant ()
    {
        Contract.Invariant ( X != null );
        //...
    }

    for this code for原因,合同项目名称出现的应用程序。P></

    alternatively设计模式的应用,可以使用assertions合同。P></

    更新:is that the term was值得提coined connection with by梅耶尔在他的规划设计of the Eiffel语言。P></


    当我们是扔在NullReferenceExceptionis of a想接入对象属性值或空字符串时,我们是想becomes empty string和接入方法。P></

    for example:P></

  • 当method of an empty string字符串accessed:P></

    1
    2
    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  • when a property of a null对象:accessedP></

    1
    2
    3
    4
    5
    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception

  • tl;dr:尝试使用Html.Partial,而不是Renderpage

    当我试图通过发送一个模型来呈现一个视图中的视图时,我得到了Object reference not set to an instance of an object,如下所示:

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

    调试显示myotherview中的模型为空。直到我把它改成:

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

    它奏效了。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @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());
    }

        @{
            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");
            }
        }

    但是我可以运行应用程序,而不必担心这个"错误"。我可以通过改变foreach循环的结构来消除错误,如下所示:

    1
    2
    3
    @foreach(var M in MyEntities){
        ...
    }

    虽然我有一种感觉,那是因为Visual Studio误读了符号和括号。


    你能怎么办?

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

    检验参数

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

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

    1
    2
    3
    4
    5
    6
    public void DoSomething(MyObject obj) {
        if(obj == null)
        {
            throw new ArgumentNullException("obj","Need a reference to obj.");
        }
    }

    使用工具

    还有几个图书馆可以提供帮助。"例如,"resharper"可以在编写代码时向您提供警告,特别是当您使用它们的属性:notNullAttribute时。

    在"微软代码契约"中,你使用像Contract.Requires(obj != null)这样的语法,它提供运行时和编译检查:引入代码契约。

    还有"postsharp",允许您使用如下属性:

    1
    public void DoSometing([NotNull] obj)

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

    纯代码解决方案

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    [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的方式非常相似的方法,除非目的是实现完全相反的目的——不允许null。以下是一些例子:

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

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

    1
    2
    3
    4
    5
    6
    7
    Person person = new Person { Name ="John" };
    WriteName(person);

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

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

    1
    2
    3
    4
    5
    6
    Person person = GetPerson();

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

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

    1
    2
    3
    4
    5
    6
    Person person = (NotNull<Person>)GetPerson();

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

    与分机结合

    NotNull与扩展方法结合起来,可以覆盖更多的情况。下面是扩展方法的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [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;
        }
    }

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

    1
    var person = GetPerson().NotNull();

    github

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

    https://github.com/luisperezphd/not空

    相关语言功能

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

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

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

    假设countrycountry类型的对象,其属性称为State等等。如果countryStateCountyCitynull,则address will be为空,. Therefore you only have to check whether为空。

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

    像nullable一样内置?

    C有一个很好的Nullable的简写,你可以在int?这样的类型后面加一个问号,使其可以为空。

    如果C有类似于上面的NotNull结构,并有类似的速记,也许是感叹号,那就太好了!这样你就可以写一些类似的东西:public void WriteName(Person! person)


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

    它用于在执行成员访问(?)或指数(?[手术]。

    例子

    1
      var name = p?.Spouse?.FirstName;

    相当于:

    1
    2
    3
    4
    5
    6
    7
        if (p != null)
        {
            if (p.Spouse != null)
            {
                name = p.Spouse.FirstName;
            }
        }

    结果是,当p为空或p.配偶为空时,名称将为空。

    否则,变量名将被指定为p.spouse.firstname的值。

    有关详细信息:空条件运算符


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

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

    1
    2
    3
    4
    public Class myClass
    {
       public int prop1 {get;set;}
    }

    现在您可以在其他类中访问这个prop1,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    public class Demo
    {
         public void testMethod()
         {
            myClass ref = null;
            ref.prop1 = 1;  //This line throws error
         }
    }

    上面的行引发错误,因为类MyClass的引用已声明但未实例化,或者对象的实例未分配给该类的referecne。

    要解决这个问题,必须实例化(将对象赋给该类的引用)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Demo
    {
         public void testMethod()
         {
            myClass ref = null;
            ref = new myClass();
            ref.prop1 = 1;  
         }
    }


    有趣的是,本页的答案中没有一个提到两个边缘案例,如果我添加它们,希望没有人介意:

    边缘大小写1:同时访问字典

    .NET中的通用字典不是线程安全的,当您试图从两个并发线程访问密钥时,它们有时可能会抛出一个NullReference,甚至(更频繁地)抛出一个KeyNotFoundException。在这种情况下,这个例外是相当误导的。

    边缘案例2:不安全代码

    如果unsafe代码抛出了NullReferenceException,您可以查看指针变量,并检查它们是否有IntPtr.Zero或其他内容。这是相同的事情("空指针异常"),但是在不安全的代码中,变量通常被强制转换为值类型/数组等,并且您的头撞在墙上,想知道值类型如何抛出这个异常。

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


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

    假设您有一个名为student的班级。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Student
    {
        private string FirstName;
        private string LastName;
        public string GetFullName()
        {
            return FirstName + LastName;
        }
    }

    现在,考虑另一个班级,你正在尝试检索学生的全名。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class StudentInfo
    {      
        public string GetStudentName()
        {
            Student s;
            string fullname = s.GetFullName();
            return fullname;
        }        
    }

    如上述代码所示,声明student s-仅声明student类型的变量,注意,此时未实例化student类。因此,当执行语句s.getfullname()时,它将抛出NullReferenceException。


    简单来说:

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

    那么如何解决这个问题:

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

  • 如果是由于对象不存在而在某些数据库命令上导致的,则只需执行空检查并处理它:

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

  • 通过按名称查找,我的意思是一些框架允许您使用字符串查找对象,代码可能如下所示:findobject("objectname");


    如果我们考虑可以抛出此异常的常见场景,那么可以使用顶部的对象访问属性。

    前任:

    1
    2
    string postalcode=Customer.Address.PostalCode;
    //if customer or address is null , this will through exeption

    在这里,如果地址为空,那么您将获得NullReferenceException。

    因此,作为一种实践,在访问这些对象(特别是通用对象)中的属性之前,我们应该始终使用空检查。

    1
    2
    string postalcode=Customer?.Address?.PostalCode;
    //if customer or address is null , this will return null, without through a exception


    从字面上来说,修复nullreferenceexection的最简单方法有两种。如果你有一个游戏对象,例如一个附加了脚本和一个名为rb(rigidbody)的变量,这个变量在你开始游戏时将以空开始。这就是为什么您得到一个nullreferenceexection的原因,因为计算机没有存储在该变量中的数据。

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

  • 使用addcomponent>physical>rigidbody向对象添加刚体然后进入脚本,输入rb = GetComponent();。这一行代码在您的Start()Awake()函数下最有效。
  • 您可以通过编程方式添加组件,并使用一行代码同时分配变量:rb = AddComponent();
  • 进一步说明:如果希望Unity向对象添加组件,而您可能忘记添加组件,则可以在类声明上方(所有using下方的空格)键入[RequireComponent(typeof(RigidBody))]。玩得开心,玩得开心!


    如果在保存或编译生成期间收到此消息,只需关闭所有文件,然后打开任何要编译和保存的文件。

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


    要使用对象的方法和成员,首先必须创建该对象。如果您没有创建它(应该保存对象的变量没有初始化),但是您试图使用它的方法或变量,您会得到这个错误。

    有时您可能只是忘记了进行初始化。

    已编辑:new不能返回空值,但失败时激发异常。很久以前,在某些语言中是这样的,但现在不是了。感谢@John Saunders指出这一点。


    这基本上是一个空引用异常。如微软所说-

    A NullReferenceException exception is thrown when you try to access a
    member of a type whose value is null.

    那是什么意思?

    这意味着,如果任何一个成员不具备任何价值,而我们让该成员执行某项任务,那么系统无疑会抛出一条消息并说-

    "嘿,等等,那个成员没有值,所以它不能执行您要交付的任务。"

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Program
    {
        static void Main(string[] args)
        {
            string str = null;
            Console.WriteLine(str.Length);
            Console.ReadLine();
        }
    }

    上面的代码显示了一个简单的字符串,它被赋予了一个空值。

    现在,当我尝试打印字符串str的长度时,我确实得到一个未处理的类型为"System.NullReferenceException"的异常,因为成员str指向空,并且不能有任何长度的空值。

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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是由于我们不检查对象值的错误而产生的。我们经常在代码开发中不选中对象值。

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

    怎样才能避免呢?

    有各种方法和方法可以避免这种著名的例外:

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

  • 异常处理:管理此异常的重要方法之一。使用简单的try catch finally块,我们可以控制这个异常并维护它的日志。当应用程序处于生产阶段时,这非常有用。

  • 空运算符:空合并运算符和空条件运算符在设置对象、变量、属性和字段的值时也很方便。

  • 调试器:对于开发人员来说,我们拥有调试的强大武器。如果在开发过程中我们面对nullreferenceexception,我们可以使用调试器来获取异常的源。

  • 内置方法:系统方法(如getValueOrDefault()、isNullOrWhiteSpace()和isNullOrEmpty())检查空值,如果有空值,则分配默认值。

  • 这里已经有很多好的答案了。你也可以通过我博客上的例子来查看更详细的描述。

    希望这也有帮助!


    You are trying to access an object that isn't created or currently not in memory.

    当我得到这个错误时,我将以下代码添加到任何触发该错误的事件处理程序中。

    1
    if (!IsLoaded) return;

    这个!意思是"不",仅供参考。因此,如果没有加载相关对象,程序将终止执行并防止崩溃。


    可能会发生与类相关的场景。在我陈述解决方案之前,这个问题就结束了:https://stackoverflow.com/questions/43348009/unable-to-instantation-class

    注意不要实例化类:如果类中的构造函数的任何部分抛出了null reference exception,则类不会实例化。在我的例子中,它试图从web.config获取一个不存在的连接字符串。

    我实例化了一个类:

    1
    2
    ClassName myClass = new ClassName();
    myClass.RunSomeMethod();

    在类内部是一个从web.config获取连接字符串的调用。构造函数的这一部分引发了空值异常,因此myClass为空。

    如果您曾经遇到过类不实例化的情况,请尝试确保类构造函数的任何部分都不会抛出null value exception。F-11并逐步通过类,确保没有空值。