c# - ошибка - object reference not set to an instance of an object. перевод




Что такое исключение NullReferenceException и как его исправить? (20)

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

Нижняя линия

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

Как и все остальное, null передается. Если в методе «A» он равен null , возможно, метод «B» передал null методу «A».

null может иметь разные значения:

  1. Переменные объекта, которые неинициализированы и, следовательно, ни на что не указывают. В этом случае, если вы NullReferenceException к свойствам или методам таких объектов, это вызывает NullReferenceException .
  2. Разработчик намеренно использует значение null чтобы указать, что значимых доступных значений нет. Обратите внимание, что в C # есть концепция обнуляемых типов данных для переменных (например, таблицы базы данных могут иметь обнуляемые поля) - вы можете присвоить им значение null чтобы указать, что в нем нет значения, например int? a = null; int? a = null; где знак вопроса указывает на то, что разрешено хранить ноль в переменной a . Вы можете проверить это либо с помощью if (a.HasValue) {...} либо с помощью if (a==null) {...} . Обнуляемые переменные, как и a этом примере, позволяют получить доступ к значению через a.Value явно или просто как обычно через a .
    Обратите внимание, что при обращении к нему через a.Value InvalidOperationException вместо NullReferenceException если a равно null - вы должны выполнить проверку заранее, т. NullReferenceException Если a вас есть другая переменная on-nullable int b; тогда вы должны делать назначения, такие как if (a.HasValue) { b = a.Value; } if (a.HasValue) { b = a.Value; } или короче, if (a != null) { b = a; } if (a != null) { b = a; }

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

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

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

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

string foo = null;
foo.ToUpper();

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

отладка

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

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

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

Примеры

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

общий

ref1.ref2.ref3.member

Если ref1 или ref2 или ref3 равны нулю, вы получите 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.
    }
}

Если вы хотите избежать нулевой ссылки дочернего (Person), вы можете инициализировать ее в конструкторе родительского (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 будет возникать в случае ранее созданных элементов управления с обработчиками событий и т. Д., 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 Перечисление label1 перед comboBox1 , игнорируя проблемы философии проектирования, по крайней мере разрешило NullReferenceException здесь NullReferenceException .

В ролях as

var myThing = someObject as Thing;

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

LINQ FirstOrDefault () и SingleOrDefault ()

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

для каждого

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 , чтобы Debug.Assert проблему раньше, чем возникнет исключение.

Если во время разработки вы знаете, что метод может, но никогда не должен возвращать Debug.Assert() , вы можете использовать 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, чтобы обеспечить значение по умолчанию, когда они равны 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

Используйте оператор слияния null: ?? [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):

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

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 , и вызов person.Title не будет выполнен, если person.Title имеет значение null .

Конечно, вам все равно нужно проверить title на null или использовать оператор условия 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 обнаруживает эту ошибку и превращает ее в исключение нулевой разыменования.

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

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

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

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

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

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

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


Исключение NullReference - Visual Basic

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

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

Замечания:

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

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

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

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

В поисках причины

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

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

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

Вы также можете использовать Locals Window ( Debug -> Windows -> Locals ) для проверки ваших объектов.

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

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

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

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

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

Проблема в том, что Dim не создает объект CashRegister; он только объявляет переменную с именем 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 , которая существует только в этом контексте (sub). Переменная reg с уровнем модуля Scope которую вы будете использовать везде, Nothing не остается.

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

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

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

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

Массивы

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

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

Объекты провайдера данных

Работа с базами данных предоставляет множество возможностей для 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 предупреждает вас о том, что это может произойти:

При объявлении в качестве переменной уровня модуля / класса, как это имеет место в случае с 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 именем «Employee», поэтому в результате попытки 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 Class .

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

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

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

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

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

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

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

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

Здесь myWebBrowser или Document могут иметь значение Nothing или элемент formfld1 может не существовать.

UI Controls

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

Visual Basic Forms

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 Назначение приведет к немедленному ЯРДУ , потому что ничто не не имеет .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.

Теперь их легко найти, когда вы знаете, что ищете:

"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 не то же самое, что Nothing

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

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

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 as 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 коллекция должна быть инициализирована только при первом запуске приложения. Альтернативное решение - добавить начальное значение в вашу коллекцию в Project -> Settings | FooBars , сохраните проект, затем удалите поддельное значение.

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

Вы, наверное, забыли New оператора.

или же

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

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

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


A NullReferenceException выдается, когда мы пытаемся получить доступ к свойствам нулевого объекта или когда строковое значение становится пустым, а мы пытаемся получить доступ к строковым методам

Например:

  1. При обращении к строковому методу пустой строки:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  2. При обращении к свойству нулевого объекта:

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 

NullReferenceException или ссылка на объект, не установленная для экземпляра объекта, возникает, когда объект класса, который вы пытаетесь использовать, не создан Например:

Предположим, что у вас есть класс с именем Student.

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

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

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

Как видно из приведенного выше кода, оператор Student s - только объявляет переменную типа Student, обратите внимание, что на этом этапе класс Student не создается. Следовательно, когда выполняется оператор s.GetFullName () , он генерирует исключение NullReferenceException.


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

В Visual Studio это обычно легко благодаря отладчику Visual Studio .

Во-первых, убедитесь, что будет обнаружена правильная ошибка - см. Как разрешить разрыв на System.NullReferenceException в VS2010? Примечание 1

Затем либо начните с отладки (F5), либо присоедините [отладчик VS] к запущенному процессу . В некоторых случаях это может быть полезно Debugger.Break , что предложит запустить отладчик.

Теперь, когда NullReferenceException генерируется (или не обрабатывается), отладчик останавливается (помните, как установлено выше?) На строке, в которой произошло исключение. Иногда ошибку легко заметить.

Например, в следующей строке единственным кодом, который может вызвать исключение, является myString значение null. Это можно проверить, посмотрев в окно просмотра или запустив выражения в Immediate Window .

var x = myString.Trim();

В более сложных случаях, таких как следующие, вам потребуется использовать один из методов, описанных выше (Watch или Immediate Windows), чтобы проверить выражения, чтобы определить, было ли str1 NULL или str2 NULL.

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

После того, когда исключение бросок был расположено, обычно тривиален урезонить назад , чтобы выяснить , где нулевое значение было [неправильно] ввел -

Потратьте время, необходимое для понимания причины исключения. Осмотрите для нулевых выражений. Проверьте предыдущие выражения, которые могли привести к таким нулевым выражениям. Добавьте breakpoints и пошагово пройдитесь по программе. Используйте отладчик.

1 Если Break on Throws слишком агрессивен и отладчик останавливается на NPE в .NET или сторонней библиотеке, можно использовать Break on User-Unhandled для ограничения перехвата исключений. Кроме того, VS2012 представляет Just My Code, который я также рекомендую включить.

Если вы выполняете отладку с включенным Just My Code, поведение будет немного другим. При включенном Just My Code отладчик игнорирует исключительные ситуации общеязыковой среды выполнения (CLR), которые выдаются за пределы My Code и не проходят через My Code


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

Пример:

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

Ошибка исключения:

Необработанное исключение:

System.NullReferenceException: ссылка на объект не установлена ​​на экземпляр объекта. в Program.Main ()


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

Предположим, у вас есть веб-форма Contact.aspx, класс codebehind которой - «Контакт», и у вас есть имя объекта «Контакт»

Затем следующий код вызовет исключение NullReferenceException при вызове context.SaveChanges ()

Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line

Ради полноты класса DataContext

public class DataContext : DbContext 
{
    public DbSet<Contact> Contacts {get; set;}
}

и Связаться с объектом класса. Иногда классы сущностей являются частичными классами, так что вы можете расширять их и в других файлах.

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

Ошибка возникает, когда сущность и класс codebehind находятся в одном пространстве имен. Чтобы это исправить, переименуйте класс сущности или класс codebehind для Contact.aspx.

Причина, я все еще не уверен в причине. Но всякий раз, когда какой-либо из классов сущностей расширяет System.Web.UI.Page, эта ошибка возникает.

Для обсуждения взгляните на NullReferenceException в DbContext.saveChanges ()


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

См. « NullReferenceException, генерируемое при тестировании пользовательского AuthorizationAttribute » для несколько подробного примера


Если вы получаете это сообщение во время сохранения или компиляции сборки, просто закройте все файлы, а затем откройте любой файл для компиляции и сохранения.

Для меня причина была в том, что я переименовал файл, а старый файл все еще был открыт.


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

Пример:

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

здесь, если адрес равен нулю, вы получите NullReferenceException.

Таким образом, в качестве практики мы всегда должны использовать проверку null, прежде чем обращаться к свойствам в таких объектах (особенно в общем)

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

Имейте в виду, что независимо от сценария, причина всегда одинакова в .NET:

Вы пытаетесь использовать ссылочную переменную со значением Nothing / null . Когда значение равно Nothing / null для ссылочной переменной, это означает, что оно фактически не содержит ссылку на экземпляр какого-либо объекта, который существует в куче.

Вы либо никогда не присваивается то переменной, не создается экземпляр значения , присвоенного переменной, или вы установите переменную равной Nothing / null вручную, или вы назвали функцию, установите переменную Nothing / null для вас.


Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я добавлю их:

Пограничный вариант № 1: одновременный доступ к словарю

Общие словари в .NET не являются поточно-ориентированными и иногда могут выдавать NullReference или даже (чаще) KeyNotFoundException при попытке получить доступ к ключу из двух параллельных потоков. Исключение весьма обманчиво в этом случае.

Крайний случай № 2: небезопасный код

Если a NullReferenceException генерируется unsafe кодом, вы можете посмотреть на переменные-указатели и проверить их на наличие IntPtr.Zero чего-либо. Это то же самое («исключение нулевого указателя»), но в небезопасном коде переменные часто приводятся к типам значений / массивам и т. Д., И вы бьетесь головой о стену, задаваясь вопросом, как тип значения может бросить это исключение.

(Кстати, еще одна причина не использовать небезопасный код, если он вам не нужен)


На вопрос «что мне с этим делать» может быть много ответов.

Более «формальным» способом предотвращения возникновения таких ошибок при разработке является применение проекта по контракту в вашем коде. Это означает, что вам нужно установить инварианты класса и / или даже предварительные условия и постфункции в вашей системе при разработке.

Короче говоря, инварианты класса гарантируют, что в вашем классе будут некоторые ограничения, которые не будут нарушаться при обычном использовании (и, следовательно, класс не попадет в противоречивое состояние). Предварительные условия означают, что данные, переданные в качестве входных данных для функции / метода, должны соответствовать некоторым установленным ограничениям и никогда не нарушать их, а постусловия означают, что выходные данные функции / метода должны повторять установленные ограничения, не нарушая их. Условия контракта никогда не должны нарушаться во время выполнения безошибочной программы, поэтому проектирование по контракту проверяется на практике в режиме отладки, в то время как отключено в выпусках , чтобы максимизировать производительность разработанной системы.

Таким образом, вы можете избежать NullReferenceException случаев, которые являются результатом нарушения установленных ограничений. Например, если вы используете свойство объекта X в классе, а затем пытаетесь вызвать один из его методов и X имеет нулевое значение, то это приведет к NullReferenceException :

public X { get; set; }

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

Но если вы установите «свойство X никогда не должно иметь нулевое значение» в качестве предварительного условия метода, то вы можете предотвратить описанный выше сценарий:

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant () 
{
    Contract.Invariant ( X != null );
    //...
}

По этой причине проект Code Contracts существует для приложений .NET.

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

ОБНОВЛЕНИЕ: Стоит отметить, что этот термин был придуман Бертраном Мейером в связи с его разработкой языка программирования Eiffel .


Ну, простыми словами:

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

Итак, как справиться с этим:

  1. Отладьте и дайте отладчику сломаться ... Он напрямую приведет вас к сломанной переменной ... Теперь ваша задача просто исправить это ... Используя ключевое слово new в соответствующем месте.

  2. Если это вызвано в некоторых командах базы данных, потому что объект не присутствует, тогда все, что вам нужно сделать, это сделать нулевую проверку и обработать ее:

    if (i == null) {
        // Handle this
    }
  3. Сложнее всего ... если GC собрал объект ... Обычно это происходит, если вы пытаетесь найти объект с помощью строк ... То есть, находя его по имени объекта, может случиться так, что GC может уже Вычистил это ... Это трудно найти и станет большой проблемой ... Лучший способ справиться с этим - делать нулевые проверки везде, где это необходимо во время процесса разработки. Это сэкономит вам много времени.

Под поиском по имени я подразумеваю некоторую инфраструктуру, позволяющую вам FIndObjects использовать строки, и код может выглядеть следующим образом: FindObject ("ObjectName");


Строка ошибки «Ссылка на объект не установлена ​​для экземпляра объекта.» Гласит, что вы не назначили объект экземпляра для ссылки на объект, и все же вы получаете доступ к свойствам / методам этого объекта.

например: допустим, у вас есть класс с именем myClass, и он содержит одно свойство prop1.

public Class myClass
{
   public int prop1 {get;set;}
}

Теперь вы получаете доступ к этому prop1 в каком-то другом классе, как показано ниже:

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref.prop1 = 1;  //This line throws error
     }
}

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

Чтобы это исправить, вы должны создать экземпляр (назначить объект для ссылки на этот класс).

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

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

При работе на разных уровнях , например, в приложении MVC, контроллеру нужны сервисы для вызова бизнес-операций. В таких сценариях Dependency Injection Container может использоваться для инициализации сервисов, чтобы избежать исключения NullReferenceException . Таким образом, это означает, что вам не нужно беспокоиться о проверке на null и просто вызывать сервисы из контроллера, как если бы они всегда были доступны (и инициализированы) в виде одиночного или прототипа.

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

Это означает, что ваш код использовал переменную ссылки на объект, для которой было установлено значение null (то есть он не ссылался на фактический экземпляр объекта).

Чтобы предотвратить ошибку, объекты, которые могут иметь значение NULL, должны быть проверены на NULL перед использованием.

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

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

SqlConnection connection = null;
connection.Open();

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

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

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

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


TL; DR: попробуйте использовать Html.Partial вместо Renderpage

Я получал, Object reference not set to an instance of an object когда пытался визуализировать представление в представлении, отправив ему модель, например так:

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

Отладка показала, что модель была нулевой внутри MyOtherView. Пока я не изменил это на:

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

И это сработало.

Более того, причина, по которой мне не нужно Html.Partial было начинать, заключалась в том, что Visual Studio иногда бросает волнистые строки, выглядящие как ошибки, Html.Partial если они находятся внутри foreach цикла, построенного по-другому , даже если это не является ошибкой:

@inherits System.Web.Mvc.WebViewPage
@{
    ViewBag.Title = "Entity Index";
    List<MyEntity> MyEntities = new List<MyEntity>();
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
}
<div>
    @{
        foreach(var M in MyEntities)
        {
            // Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
            @Html.Partial("MyOtherView.cshtml");
        }
    }
</div>

Но я смог запустить приложение без проблем с этой «ошибкой». Я смог избавиться от ошибки, изменив структуру foreach цикла так:

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

Хотя у меня такое чувство, что Visual Studio неправильно истолковывает амперсанды и скобки.


Что вы можете с этим поделать?

Здесь есть много хороших ответов, объясняющих, что такое пустая ссылка и как ее отладить. Но есть очень мало о том, как предотвратить проблему или, по крайней мере, облегчить ее обнаружение.

Проверьте аргументы

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

Конструктор функции ArgumentNullException Even принимает имя параметра и сообщение в качестве аргумента, чтобы вы могли точно сказать разработчику, в чем заключается проблема.

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

Используйте инструменты

Есть также несколько библиотек, которые могут помочь. Например, «Resharper» может выдавать вам предупреждения во время написания кода, особенно если вы используете их атрибут: NotNullAttribute

Там в «Microsoft Code Контракты» , где вы используете синтаксис , как Contract.Requires(obj != null) открывающее во время выполнения и компиляции проверочные: Введение в контракты кода .

Также есть «PostSharp», который позволит вам просто использовать такие атрибуты:

public void DoSometing([NotNull] obj)

Сделав это и сделав PostSharp частью вашего процесса сборки, obj вы будете проверяться на null во время выполнения. Смотрите: PostSharp нулевая проверка

Решение с простым кодом

Или вы всегда можете написать свой собственный подход, используя простой старый код. Например, вот структура, которую вы можете использовать для перехвата нулевых ссылок. Он смоделирован по той же концепции, что и Nullable<T> :

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

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

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

            _value = value;
        }
    }

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

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

Вы будете использовать очень похоже на то же самое, что и вы Nullable<T> , за исключением того, что вы делаете совершенно противоположное - не допустить null . Вот некоторые примеры:

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

NotNull<T> неявно приведен к и от, T так что вы можете использовать его где угодно. Например, вы можете передать Person объект в метод, который принимает NotNull<Person> :

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

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

Как вы можете видеть выше, как с nullable, вы получите доступ к базовому значению через Value свойство. В качестве альтернативы, вы можете использовать явное или неявное приведение, вы можете увидеть пример с возвращаемым значением ниже:

Person person = GetPerson();

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

Или вы можете даже использовать его, когда метод просто возвращает T (в данном случае Person ), выполняя приведение. Например, следующий код будет похож на приведенный выше код:

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

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

Объединить с расширением

Объедините NotNull<T> с методом расширения, и вы сможете охватить еще больше ситуаций. Вот пример того, как может выглядеть метод расширения:

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

        return @this;
    }
}

И вот пример того, как это можно использовать:

var person = GetPerson().NotNull();

GitHub

Для справки, я сделал приведенный выше код доступным на GitHub, вы можете найти его по адресу:

https://github.com/luisperezphd/NotNull

Родственный язык

В C # 6.0 введен «нулевой оператор», который немного помогает в этом. С помощью этой функции вы можете ссылаться на вложенные объекты и, если какой-либо из них является null целым выражением, возвращается null .

Это уменьшает количество нулевых проверок, которые вы должны сделать в некоторых случаях. Синтаксис ставит знак вопроса перед каждой точкой. Возьмите следующий код для примера:

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

Представьте, что country это объект типа Country , у которого есть свойство с именем State и так далее. Если country , State , County или City это null то address will be нулевой . Therefore you only have to check whether адрес is null`.

Это отличная функция, но она дает вам меньше информации. Это не делает очевидным, какой из 4 является нулевым.

Встроенный как Nullable?

В C # есть неплохое сокращение Nullable<T> , вы можете сделать что-то обнуляемое, поставив знак вопроса после типа, например int? ,

Было бы хорошо , если бы C # было что - то вроде NotNull<T> структуры выше и имел подобную стенографии, может быть восклицательным знаком , так что вы могли бы написать что - то вроде (!) public void WriteName(Person! person) .







nullreferenceexception