Вопрос: Что такое исключение NullReferenceException и как его исправить?


У меня есть код, и когда он выполняется, он бросает NullReferenceException, говоря:

В экземпляре объекта не задана ссылка на объект.

Что это значит и что я могу сделать, чтобы исправить эту ошибку?


1880


источник


Ответы:


В чем причина?

Нижняя линия

Вы пытаетесь использовать что-то, что null(или Nothingв VB.NET). Это означает, что вы либо null, или вы никогда не устанавливали его на что-либо вообще.

Как и все остальное, nullпроходит мимо. Если это null в метод «А», может быть, что метод «В» прошел null в метод «А».

В остальной части этой статьи более подробно рассматриваются ошибки, которые часто делают многие программисты, которые могут привести к NullReferenceException,

Более конкретно

Время выполнения NullReferenceException всегда означает одно и то же: вы пытаетесь использовать ссылку, и ссылка не инициализируется (или это было один раз инициализирован, но больше никогда инициализируется).

Это означает, что ссылка null, и вы не можете получить доступ к элементам (таким как методы) через nullСправка. Простейший случай:

string foo = null;
foo.ToUpper();

Это вызовет NullReferenceExceptionна второй строке, потому что вы не можете вызвать метод экземпляра ToUpper()на stringссылка на null,

отладка

Как вы находите источник NullReferenceException? Помимо рассмотрения самого исключения, которое будет выбрасываться именно в том месте, где оно происходит, применяются общие правила отладки в Visual Studio: размещайте стратегические точки останова и проверять переменные , либо наведя указатель мыши на их имена, открыв окно (Quick) Watch, либо используя различные панели отладки, такие как Locals and Autos.

Если вы хотите узнать, где ссылка или не установлена, щелкните ее имя правой кнопкой мыши и выберите «Найти все ссылки». Затем вы можете разместить точку останова в каждом найденном месте и запустить свою программу с прикрепленным отладчиком. Каждый раз, когда отладчик разбивается на такую ​​точку останова, вам нужно определить, хотите ли вы, чтобы ссылка была не нулевой, проверите переменную и убедитесь, что она указывает на экземпляр, когда вы ожидаете этого.

Таким образом, следуя программному потоку, вы можете найти место, где экземпляр не должен быть нулевым, и почему он неправильно установлен.

Примеры

Некоторые распространенные сценарии, в которых может быть выбрано исключение:

общий

ref1.ref2.ref3.member

Если ref1 или ref2 или ref3 равно null, вы получите NullReferenceException, Если вы хотите решить проблему, то выясните, какой из них является нулевым, переписывая выражение в его более простой эквивалент:

var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member

В частности, в HttpContext.Current.User.Identity.Name, HttpContext.Currentможет быть нулевым, или Userсвойство может быть нулевым, или Identityсвойство может быть нулевым.

непрямой

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.
    }
}

Если вы хотите избежать ссылки на null (Person), вы можете инициализировать его в конструкторе объекта parent (Book).

Вложенные инициализаторы объектов

То же самое относится к инициализаторам вложенных объектов:

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

Это переводит

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

В то время newиспользуется ключевое слово, оно создает только новый экземпляр Book, но не новый экземпляр Person, Итак Authorсобственность по-прежнему null,

Инициализаторы вложенной коллекции

public class Person {
    public ICollection<Book> Books { get; set; }
}
public class Book {
    public string Title { get; set; }
}

Инициализаторы вложенных коллекций ведут себя одинаково:

Person p1 = new Person {
    Books = {
        new Book { Title = "Title1" },
        new Book { Title = "Title2" },
    }
};

Это переводит

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

new Personсоздает только экземпляр Person, но Booksсбор по-прежнему null, Синтаксис инициализатора коллекции не создает коллекцию для p1.Books, он переводит только p1.Books.Add(...)заявления.

массив

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

Элементы массива

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.

Жесткие массивы

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.

Коллекция / Список / Словарь

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

Переменная диапазона (косвенная / отложенная)

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.

Мероприятия

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

Плохие соглашения об именах:

Если вы назвали поля по-разному от местных, вы, возможно, поняли, что вы никогда не инициализировали это поле.

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);
    }
}

Это можно решить, выполнив соглашение с префиксными полями с помощью подчеркивания:

private Customer _customer;

Жизненный цикл страницы ASP.NET:

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

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

Если исключение возникает при ссылке на свойство @Modelв представлении ASP.NET MVC вам нужно понять, что Modelустанавливается в вашем методе действий, когда вы returnвид. Когда вы возвращаете пустую модель (или свойство модели) с вашего контроллера, исключение возникает, когда представления обращаются к нему:

// 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> <!-- Also throws -->

Порядок создания WPF-контроля и события

Элементы управления WPF создаются во время вызова InitializeComponentв порядке их появления в визуальном дереве. NullReferenceExceptionбудут подняты в случае ранних созданных элементов управления с обработчиками событий и т. д., которые срабатывают во время InitializeComponentкоторые ссылаются на поздние созданные элементы управления.

Например :

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

Вот comboBox1создается до label1, Если comboBox1_SelectionChangedпытается ссылаться на `label1, он еще не создан.

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

Изменение порядка объявлений в XAML (т. Е. Перечисление label1до comboBox1, игнорируя вопросы философии дизайна, по крайней мере разрешит NullReferenceExceptionВот.

Бросьте as

var myThing = someObject as Thing;

Это не вызывает InvalidCastException, но возвращает nullкогда сбой завершается (и когда someObject сам по себе является нулевым). Поэтому имейте это в виду.

LINQ FirstOrDefault () и SingleOrDefault ()

Простые версии First()а также Single()бросать исключения, когда ничего нет. В этом случае версии «OrDefault» возвращают null. Поэтому имейте это в виду.

для каждого

foreachбросает при попытке итерации нулевой коллекции. Обычно вызвано неожиданным nullрезультат методов, возвращающих коллекции.

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

Более реалистичный пример - выбор узлов из XML-документа. Будут выбрасываться, если узлы не найдены, но первоначальная отладка показывает, что все свойства действительны:

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

Способы избежать

Явно проверяю nullи игнорировать нулевые значения.

Если вы ожидаете, что ссылка иногда будет нулевой, вы можете проверить ее nullперед доступом к членам экземпляра:

void PrintName(Person p) {
    if (p != null) {
        Console.WriteLine(p.Name);
    }
}

Явно проверяю nullи укажите значение по умолчанию.

Способы, на которые вы рассчитываете вернуть экземпляр, могут вернуться null, например, когда искомый объект не может быть найден. Вы можете выбрать значение по умолчанию, если это так:

string GetCategory(Book b) {
    if (b == null)
        return "Unknown";
    return b.Category;
}

Явно проверяю nullот вызовов методов и выброса пользовательского исключения.

Вы также можете создать настраиваемое исключение, только чтобы поймать его в вызывающем коде:

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()чтобы как можно скорее сломаться, когда это произойдет:

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снова, когда book == nullво время выполнения в режиме выпуска.

использование GetValueOrDefault()для типов значений с нулевым значением для предоставления значения по умолчанию, когда они null,

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

Используйте оператор нулевого коалесцирования: ??[C #] или If()[VB].

Сокращение до значения по умолчанию, когда nullвстречается:

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):

Это также иногда называют безопасной навигацией или Элвисом (после его формы). Если выражение в левой части оператора равно NULL, то правая сторона не будет оцениваться, а вместо него будет возвращена нуль. Это означает такие случаи:

var title = person.Title.ToUpper();

Если у человека нет названия, это вызовет исключение, поскольку оно пытается вызвать ToUpperна свойство с нулевым значением.

В C # 5 и ниже это можно защитить:

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

Теперь переменная title будет равна null вместо исключения исключения. C # 6 вводит более короткий синтаксис для этого:

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

Это приведет к тому, что переменная title будет null, и призыв к ToUpperне состоит из person.Titleявляется null,

Конечно ты все еще нужно проверить titleдля null или использовать оператор нулевого условия вместе с нулевым коалесцирующим оператором ( ??) для подачи значения по умолчанию:

// 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]следующим образом:

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

Это сделает следующее: Если myIntArray имеет значение null, выражение возвращает null, и вы можете спокойно его проверить. Если он содержит массив, он будет делать то же самое, что: elem = myIntArray[i];и возвращает i го элемент.

Специальные методы для отладки и исправления нулевых дереф в итераторах

C # поддерживает «блоки итератора» (называемые «генераторами» на некоторых других популярных языках). Исключения исключений в виде исключений могут быть особенно сложными для отладки в блоках итератора из-за отложенного выполнения:

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будет бросать. Теперь вы можете подумать, что правильно это сделать:

// 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просто возвращает объект, который при повторении будет запускать блок итератора.

При написании нулевой проверки, подобной этой, вы предотвращаете нулевое разыменование, но вы перемещаете исключение нулевого аргумента в точку итерация , а не вызов , и это очень запутанно отлаживать ,

Правильное исправление:

// 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();
}

То есть, создайте частный вспомогательный метод, который имеет логику блока итератора, и метод открытой поверхности, который выполняет нулевую проверку и возвращает итератор. Теперь, когда GetFrobs, нулевая проверка происходит немедленно, а затем GetFrobsForRealвыполняется, когда последовательность повторяется.

Если вы изучите источник ссылок для LINQ to Objects, вы увидите, что этот метод используется повсюду. Это немного сложнее писать, но значительно облегчает отладку ошибок с ошибками. Оптимизируйте свой код для удобства вызывающего абонента, а не для удобства автора ,

Замечание о нулевых различиях в небезопасном коде

C # имеет «небезопасный» режим, который, как следует из названия, чрезвычайно опасен, поскольку обычные механизмы безопасности, обеспечивающие безопасность памяти и безопасность типов, не применяются. Вы не должны писать небезопасный код, если у вас нет глубокого понимания того, как работает память ,

В небезопасном режиме вы должны знать два важных факта:

  • разыменование нулевого указатель приводит к тому же исключению, что и разыменование нулевого Справка
  • разыменование недействительного указателя, не равного нулю Можно производить это исключение в некоторых случаях

Чтобы понять, почему это так, это помогает понять, как .NET в первую очередь создает исключения нулевой разницы. (Эти сведения относятся к .NET, работающему в Windows, другие операционные системы используют аналогичные механизмы.)

Память виртуализирована в Windows; каждый процесс получает пространство виртуальной памяти из многих «страниц» памяти, которые отслеживаются операционной системой. На каждой странице памяти установлены флаги, определяющие способ их использования: чтение, запись, выполнение и т. Д. низший страница отмечена как «выдает ошибку, если она когда-либо используется».

И нулевой указатель, и нулевая ссылка в C # внутренне представлены как числовое значение нуль, и поэтому любая попытка разыменовать его в соответствующем хранилище памяти приводит к тому, что операционная система создает ошибку. Затем среда выполнения .NET обнаруживает эту ошибку и превращает ее в исключение нулевого исключения.

Вот почему разыменование нулевого указателя и нулевой ссылки приводит к тому же исключению.

Как насчет второго пункта? Разыменование Любые неверный указатель, который попадает на самую низкую страницу виртуальной памяти, вызывает ту же ошибку операционной системы и, тем самым, одно и то же исключение.

Почему это имеет смысл? Ну, предположим, что у нас есть структура, содержащая два ints, а неуправляемый указатель равен нулю. Если мы попытаемся разыменовать второй int в структуре, CLR не будет пытаться получить доступ к хранилищу при нулевом местоположении; он получит доступ к хранилищу в четвертом месте. Но логически это нулевое разыменование, потому что мы добираемся до этого адреса с помощью null.

Если вы работаете с небезопасным кодом, и вы получаете исключение с помощью исключающего исключения, просто имейте в виду, что указатель-нарушитель не должен иметь значение null. Это может быть любое местоположение на самой низкой странице, и это исключение будет создано.


2075



NullReferenceException - Visual Basic

NullReference Exceptionдля Visual Basic ничем не отличается от C # , В конце концов, они оба сообщают об одном и том же исключении, определенном в .NET Framework, которое они оба используют. Причины, уникальные для Visual Basic, встречаются редко (возможно, только один).

В этом ответе будут использоваться термины, синтаксис и контекст Visual Basic. Используемые примеры исходят из большого количества прошлых вопросов о переполнении стека. Это должно быть максимально релевантным с помощью виды часто встречающихся в сообщениях. Немного больше объяснений также предоставляется тем, кому это может понадобиться. Пример, аналогичный вашему очень вероятно, перечисленные здесь.

Заметка:

  1. Это основано на концепции: в вашем проекте нет кода для вставки. Он призван помочь вам понять, какие причины NullReferenceException(NRE), как его найти, как его исправить, и как его избежать. NRE может быть вызвано многими способами, поэтому это вряд ли будет вашей единственной встречей.
  2. Примеры (из Stack Overflow posts) не всегда показывают лучший способ сделать что-то в первую очередь.
  3. Как правило, используется самое простое средство.

Основное значение

Сообщение Msgstr "Объект не установлен в экземпляр объекта" означает, что вы пытаетесь использовать объект, который не был инициализирован. Это сводится к одному из следующих:

  • Ваш код объявленный переменная объекта, но она не инициализировать он (создать экземпляр или " иллюстрировать примерами ' Это)
  • Что-то, что предполагал ваш код, инициализирует объект, не
  • Возможно, другой код преждевременно аннулировал объект, который все еще используется

Поиск причины

Поскольку проблема - это ссылка на объект, которая Nothing, ответ должен изучить их, чтобы выяснить, какой из них. Затем определите, почему он не инициализирован. Держите мышь над различными переменными, и Visual Studio (VS) покажет их значения - виновник будет Nothing,

IDE debug display

Вы также должны удалить блоки Try / Catch из соответствующего кода, особенно те, где в блоке Catch ничего нет. Это приведет к сбою вашего кода при попытке использовать объект, который Nothing, Это то, что вы хотите потому что он будет определять точные место нахождения проблемы и позволяют идентифицировать вызывающий объект.

MsgBoxв Catch, который отображает Error while...будет мало помогать. Этот метод также приводит к очень плохо Вопросы переполнения стека, потому что вы не можете описать фактическое исключение, связанный с ним объект или даже строку кода, где это происходит.

Вы также можете использовать Locals Window( Отладка -> Windows -> Местные жители ), чтобы исследовать ваши объекты.

Как только вы знаете, что и где проблема, обычно довольно легко исправить и быстрее, чем опубликовать новый вопрос.

Смотрите также:

Примеры и средства защиты

Объекты класса / Создание экземпляра

Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE

Проблема в том, что Dimне создает кассовый аппарат объект ; он объявляет только переменную с именем regэтого типа. декларирование переменной объекта и создания пример это две разные вещи.

средство

Newоператор может часто использоваться для создания экземпляра, когда вы его объявляете:

Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

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

Когда целесообразно создать экземпляр позже:

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

Заметка: Не использование Dimснова в процедуре, включая конструктор ( Sub New):

Private reg As CashRegister
'...

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

Это создаст местный переменная, reg, который существует только в этом контексте (под). regпеременная с уровнем модуля Scopeкоторый вы будете использовать везде, остаётся Nothing,

Отсутствует Newоператор является причиной №1 NullReference Exceptionsрассмотренные в вопросах переполнения стека.

Visual Basic пытается многократно очистить процесс, используя New: Используя NewОператор создает новый объект и вызовы Sub New- конструктор - где ваш объект может выполнять любую другую инициализацию.

Чтобы быть ясным, Dim(или Private) только объявляет переменная и ее Type, Объем переменной - независимо от того, существует ли она для всего модуля / класса или является локальной процедурой, - определяется где это объявлено. Private | Friend | Publicопределяет уровень доступа, а не Объем ,

Для получения дополнительной информации см.


Массивы

Массивы также должны быть созданы:

Private arr as String()

Этот массив только объявлен, а не создан. Существует несколько способов инициализации массива:

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, при инициализации локального массива с использованием литерала и Option Infer, As <Type>а также Newэлементы являются необязательными:

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}

Тип данных и размер массива выводятся из назначаемых данных. Декларации уровня класса / модуля по-прежнему требуют As <Type>с Option Strict:

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

Пример: массив объектов класса

Dim arrFoo(5) As Foo

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

Массив создан, но Fooобъектов в нем нет.

средство

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)будет довольно сложно иметь элемент без действительного объекта:

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

Для получения дополнительной информации см.


Списки и коллекции

Коллекции .NET (из которых есть много разновидностей - Списки, Словарь и т. Д.) Также должны быть созданы или созданы.

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

Вы получаете то же исключение по той же причине - myListбыл объявлен, но экземпляр не создан. Средство защиты одно и то же:

myList = New List(Of String)

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

Общий надзор - это класс, который использует коллекцию Type:

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, Возможно, это было намерение сделать это в конструкторе:

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

Как и раньше, это неверно:

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

Для получения дополнительной информации см. List(Of T)Класс ,


Объекты поставщика данных

Работа с базами данных предоставляет много возможностей для NullReference, поскольку может быть много объектов ( Command, Connection, Transaction, Dataset, DataTable, DataRows....) в использовании сразу. Заметка: Неважно, какой поставщик данных вы используете: MySQL, SQL Server, OleDB и т. Д. - концепции одинаковы.

Пример 1

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Объект Dataset был объявлен, но экземпляр никогда не создавался. DataAdapterзаполнит существующую DataSet, а не создать его. В этом случае, поскольку dsявляется локальной переменной, IDE предупреждает вас что это может произойти:

img

Когда объявляется как переменная уровня модуля / класса, как это имеет место в случае с con, компилятор не может знать, был ли объект создан процедурой восходящего потока. Не игнорируйте предупреждения.

средство

Dim ds As New DataSet

Пример 2.

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)

Опечатка - проблема здесь: Employeesпротив Employee, Не было DataTableс именем «Сотрудник», поэтому NullReferenceExceptionрезультаты пытаются получить к нему доступ. Другая потенциальная проблема заключается в том, что Itemsчто может быть и так, когда SQL включает предложение WHERE.

средство

Поскольку это использует одну таблицу, используя Tables(0)во избежание ошибок орфографии. Изучение Rows.Countтакже может помочь:

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которые также могут быть протестированы:

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

Пример 3.

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")ссылается на несуществующую таблицу.

средство то же самое, ссылайтесь на таблицу по индексу:

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

Смотрите также Класс DataTable ,


Пути объектов / Вложенные

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

Код только для тестирования Itemsтогда как оба myFooа также Barтакже может быть Ничто. средство - проверять всю цепочку или путь объектов по одному за раз:

If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....

AndAlsoэто важно. Последующие тесты не будут выполняться после первого Falseусловие встречается. Это позволяет коду безопасно «сверлить» в объект (ы) один «уровень» за раз, оценивая myFoo.Barтолько после (и если) myFooопределяется как действительный. Цепочки объектов или пути могут быть довольно длинными при кодировании сложных объектов:

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

Невозможно ссылаться на что-либо «вниз по течению» nullобъект. Это также относится к элементам управления:

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

Вот, myWebBrowserили Documentможет быть Ничто или formfld1элемент может не существовать.


Элементы управления пользовательским интерфейсом

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)

Помимо всего прочего, этот код не предполагает, что пользователь может не выбрать что-либо в одном или нескольких элементах управления пользовательским интерфейсом. ListBox1.SelectedItemвполне может быть Nothing, так ListBox1.SelectedItem.ToStringприведет к NRE.

средство

Проверяйте данные перед их использованием (также используйте Option Strictи параметры SQL):

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


Визуальные базовые формы

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. Это также сложно, потому что это может привести к отказу каскада.

Таким образом, массивы и коллекции не могут быть инициализированы. Этот код инициализации будет запущен до конструктор создает Formили Controls, В результате:

  • Списки и коллекция будут просто пустыми
  • Массив будет содержать пять элементов Nothing
  • somevarприсвоение приведет к немедленному NRE, потому что ничего не имеет .Textимущество

После этого ссылки на элементы массива приведут к NRE. Если вы сделаете это в Form_Load, из-за нечетной ошибки, IDE может нет сообщать об исключении, когда это происходит. Исключение будет всплывать позже когда ваш код пытается использовать массив. Это «молчаливое исключение» подробно в этом сообщении , Для наших целей ключ заключается в том, что когда что-то катастрофическое происходит при создании формы ( Sub Newили Form Loadсобытие), исключения могут не сообщаться, код выходит из процедуры и просто отображает форму.

Поскольку в вашем Sub Newили Form Loadсобытие будет запущено после NRE, многое другое могут быть оставлены неинициализированными.

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

Заметка это относится к любым и всем ссылкам управления и компонентов, что делает их незаконными там, где они есть:

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вызов:

' 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

Код массива, возможно, еще не вышел из леса. Любые элементы управления, которые находятся в управлении контейнером (например, GroupBoxили Panel) не будет найдено в Me.Controls; они будут находиться в коллекции Controls этой Panel или GroupBox. Также не будет возвращен элемент управления, если контрольное имя ошибочно написано ( "TeStBox2"). В таких случаях, Nothingбудет снова сохранена в этих элементах массива, и при попытке ссылаться на нее будет получена NRE.

Теперь вам будет легко найти, что вы знаете, что ищете: VS shows you the error of your ways

«Button2» находится на Panel

средство

Вместо косвенных ссылок по имени, используя форму Controlsсбор, используйте контрольную ссылку:

' 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...})

Функция, возвращающая ничего

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может привести ». Вы можете подавить предупреждение, заменив Exit Functionс Return Nothing, но это не решает проблему. Все, что пытается использовать возврат, когда someCondition = Falseприведет к NRE:

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

средство

замещать Exit Functionв функции с Return bList, Возвращение пустой Listэто не то же самое, что возвращение Nothing, Если есть вероятность, что возвращаемый объект может быть Nothing, перед использованием:

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

Плохо реализовано Try / Catch

Плохо реализованный Try / Catch может скрыть, где проблема, и привести к новым:

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блок - площадка для дьявола. Этот ОП был озадачен тем, почему он получал NRE в Finallyблок. В других ситуациях пустая Catchможет привести к чему-то еще значительно дальше вниз по течению и сэкономить время, глядя на неправильные вещи в неподходящем месте проблемы. («Тихая исключение», описанное выше, дает такое же развлекательное значение.)

средство

Не используйте пустые блоки Try / Catch - пусть сбой кода, чтобы вы могли: a) определить причину b) определить местоположение и c) применить надлежащее средство. Блоки Try / Catch не предназначены для того, чтобы скрывать исключения от лица, обладающего уникальной квалификацией для их исправления - разработчика.


DBNull - это не то же самое, что и ничего

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

IsDBNullфункция используется для проверки того, стоимость равняется System.DBNull: Из MSDN:

Значение System.DBNull указывает, что объект представляет отсутствующие или несуществующие данные. DBNull - это не то же самое, что и Nothing, что указывает на то, что переменная еще не была инициализирована.

средство

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

Как и прежде, вы можете протестировать Nothing, а затем для определенного значения:

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

Пример 2.

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:

If getFoo IsNot Nothing Then...

управления

Dim chk As CheckBox

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

Если CheckBoxс chkNameне может быть найден (или существует в GroupBox), тогда chkбудет Nothing, и попытка ссылаться на любое свойство приведет к исключению.

средство

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

DataGridView

Периодически DGV имеет несколько причуд:

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"

Если dgvBooksимеет AutoGenerateColumns = True, он будет создавать столбцы, но он их не назовет, поэтому приведенный выше код не удастся, когда он ссылается на них по имени.

средство

Назовите столбцы вручную или ссылку по индексу:

dgvBooks.Columns(0).Visible = True

Пример 2 - Остерегайтесь NewRow

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

Когда ваш DataGridViewимеет AllowUserToAddRowsв виде True(по умолчанию), Cellsв пустой / новой строке внизу все будут содержать Nothing, Большинство попыток использования содержимого (например, ToString) приведет к NRE.

средство

Использовать For/Eachи проверить IsNewRowчтобы определить, является ли это последней строкой. Это работает независимо от того, AllowUserToAddRowsэто правда или нет:

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

Если вы используете For nцикл, изменить количество строк или использовать Exit Forкогда IsNewRowправда.


My.Settings (StringCollection)

При определенных обстоятельствах, пытаясь использовать элемент из My.Settingsкоторый является StringCollectionможет привести к NullReference при первом использовании. Решение одно и то же, но не так очевидно. Рассматривать:

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

Поскольку VB управляет настройками для вас, разумно ожидать, что он инициализирует коллекцию. Он будет, но только если вы ранее добавили начальную запись в коллекцию (в редакторе настроек). Поскольку коллекция (по-видимому) инициализируется при добавлении элемента, остается Nothingкогда в редакторе настроек нет элементов.

средство

Инициализировать коллекцию настроек в форме Loadобработчик события, если / когда необходимо:

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

Как правило, Settingsсбор должен быть инициализирован только при первом запуске приложения. Альтернативным средством является добавление начального значения в вашу коллекцию в Проект -> Настройки | FooBars , сохраните проект, затем удалите поддельное значение.


Ключевые моменты

Вы, наверное, Newоператор.

или

То, что вы предположили, безупречно выполнило бы, чтобы вернуть инициализированный объект в ваш код, не сделал этого.

Не игнорируйте предупреждения компилятора (когда-либо) и используйте Option Strict On(всегда).


Исключение NullReference MSDN


271



Другим сценарием является то, что вы бросаете нулевой объект в тип значения , Например, приведенный ниже код:

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

Он бросит NullReferenceExceptionна литье. В приведенном выше примере это кажется совершенно очевидным, но это может произойти в более «поздних связующих» сложных сценариях, где нулевой объект был возвращен из некоторого кода, которого вы не являетесь, и приведение, например, генерируется некоторой автоматической системой.

Одним из примеров этого является этот простой фрагмент привязки ASP.NET с элементом управления календарем:

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

Вот, SelectedDateна самом деле является свойством DateTimeтип - CalendarТип веб-управления, и привязка может отлично вернуть что-то null. Неявный генератор ASP.NET создаст кусок кода, который будет эквивалентен приведенному выше методу. И это поднимет NullReferenceExceptionэто довольно сложно определить, потому что он лежит в сгенерированном ASP.NET коде, который компилируется отлично ...


215



Это означает, что рассматриваемая переменная ничем не указана. Я мог бы создать это так:

SqlConnection connection = null;
connection.Open();

Это вызовет ошибку, потому что, пока я объявил переменную " connection", это ни на что не указывает. Когда я пытаюсь вызвать участника" Openmsgstr "нет ссылок для его разрешения, и это вызовет ошибку.

Чтобы избежать этой ошибки:

  1. Всегда инициализируйте свои объекты, прежде чем пытаться что-либо сделать с ними.
  2. Если вы не уверены, является ли объект нулевым, проверьте его с помощью object == null,

Инструмент Resharper JetBrains определит каждое место в вашем коде, которое имеет возможность ошибки нулевой ссылки, позволяя вам ввести нулевую проверку. Эта ошибка является источником ошибок номер один, IMHO.


143



Это означает, что ваш код использовал ссылочную переменную объекта, которая была установлена ​​в 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
}

134



Be aware that regardless of the scenario, the cause is always the same in .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.


89



An example of this exception being thrown is: When you are trying to check something, that is null.

For example:

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
} 

The .NET runtime will throw a NullReferenceException when you attempt to perform an action on something which hasn't been instantiated i.e. the code above.

In comparison to an ArgumentNullException which is typically thrown as a defensive measure if a method expects that what is being passed to it is not null.

More information is in C# NullReferenceException and Null Parameter.


75



If you have not initialized a reference type, and you want to set or read one of its properties, it will throw a NullReferenceException.

Example:

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

You can simply avoid this by checking if the variable is not null:

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

To fully understand why a NullReferenceException is thrown, it is important to know the difference between value types and reference types.

So, if you're dealing with value types, NullReferenceExceptions can not occur. Though you need to keep alert when dealing with reference types!

Only reference types, as the name is suggesting, can hold references or point literally to nothing (or 'null'). Whereas value types always contain a value.

Reference types (these ones must be checked):

  • dynamic
  • object
  • string

Value types (you can simply ignore these ones):

  • Numeric types
  • Integral types
  • Floating-point types
  • decimal
  • bool
  • User defined structs

71



Another case where NullReferenceExceptions can happen is the (incorrect) use of the as operator:

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

Here, Book and Car are incompatible types; a Car cannot be converted/cast to a Book. When this cast fails, as returns null. Using mybook after this causes a NullReferenceException.

In general, you should use a cast or as, as follows:

If you are expecting the type conversion to always succeed (ie. you know what the object should be ahead of time), then you should use a cast:

ComicBook cb = (ComicBook)specificBook;

If you are unsure of the type, but you want to try to use it as a specific type, then use as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}

64



You are using the object that contains the null value reference. So it's giving a null exception. In the example the string value is null and when checking its length, the exception occurred.

Example:

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

The exception error is:

Unhandled Exception:

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


58



Simon Mourier gave this example:

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

where an unboxing conversion (cast) from object (or from one of the classes System.ValueType or System.Enum, or from an interface type) to a value type (other than Nullable<>) in itself gives the NullReferenceException.

In the other direction, a boxing conversion from a Nullable<> which has HasValue equal to false to a reference type, can give a null reference which can then later lead to a NullReferenceException. The classic example is:

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

Sometimes the boxing happens in another way. For example with this non-generic extension method:

public static void MyExtension(this object x)
{
  x.ToString();
}

the following code will be problematic:

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

These cases arise because of the special rules the runtime uses when boxing Nullable<> instances.


47