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

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

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

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


person Community    schedule 11.01.2011    source источник
comment
Помощник по исключениям в VS 2017 будет более полезен в диагностике причины этого исключения - blogs.msdn.microsoft.com/visualstudio/2016/11/28/ в разделе New Exception Helper.   -  person Zev Spitz    schedule 29.12.2016
comment
Уважаемые будущие посетители, ответы на этот вопрос в равной степени применимы к ArgumentNullException. Если ваш вопрос был закрыт как дубликат этого, и вы столкнулись с ANE, следуйте инструкциям в ответах по отладке и устранению проблемы.   -  person    schedule 13.10.2017
comment
@will ANE должно происходить только в том случае, если в качестве параметра передается нуль. Можете ли вы привести пример, если вопрос ANE был закрыт как дубликат этого?   -  person John Saunders    schedule 15.10.2017
comment
Он появился на Meta, но мне пришлось бы поискать ссылку. Но что касается этого комментария, ANE - это просто NRE, но кто-то добавил превентивную проверку, и вы, по крайней мере, точно знаете, что имеет значение null (указано имя аргумента), поэтому его немного легче диагностировать, чем прямое NRE.   -  person    schedule 16.10.2017


Ответы (27)


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

Нижняя линия

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

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

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

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

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

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

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

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

string foo = null;
foo.ToUpper();

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

Отладка

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

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

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

Примеры

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

Generic

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 может иметь значение NULL, или свойство User может иметь значение NULL, или свойство Identity может иметь значение NULL.

Косвенный

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

Вложенная коллекция Initializers ведет себя так же:

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. Синтаксис коллекции Initializer не создает коллекцию для 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.

События (C #)

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

(Примечание: компилятор VB.NET вставляет нулевые проверки на использование событий, поэтому нет необходимости проверять события для Nothing в VB.NET.)

Неправильные соглашения об именах:

Если вы назвали поля иначе, чем локальные, вы могли понять, что никогда не инициализировали поле.

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 View, вы должны понимать, что 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(); // NullReferenceException here!!
}

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

В ролях с as

var myThing = someObject as Thing;

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

LINQ FirstOrDefault() и SingleOrDefault()

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

для каждого

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

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

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

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

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

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

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

var title = person.Title.ToUpper();

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

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

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

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

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

Это приведет к тому, что переменная заголовка будет null, и вызов ToUpper не будет выполнен, если 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 th.

Использовать нулевой контекст (доступно в C # 8):

Представленные в C# 8, пустые контексты и ссылочные типы, допускающие значение NULL, выполняют статический анализ переменных и выдают предупреждение компилятора, если значение потенциально может быть null или установлено на null. Ссылочные типы, допускающие значение NULL, позволяют явно разрешать типам null.

Контекст аннотации, допускающей значение NULL, и контекст предупреждения, допускающего значение NULL, можно установить для проекта с помощью элемента Nullable в вашем файле csproj. Этот элемент настраивает, как компилятор интерпретирует допустимость значений NULL для типов и какие предупреждения генерируются. Допустимые настройки:

  • enable: контекст аннотации, допускающей значение NULL, включен. Контекст предупреждения, допускающего значение NULL, включен. Переменные ссылочного типа, например строка, не допускают значения NULL. Все предупреждения о допустимости пустых значений включены.
  • disable: контекст аннотации, допускающей значение NULL, отключен. Контекст предупреждения, допускающего значение NULL, отключен. Переменные ссылочного типа не обращают внимания, как и более ранние версии C #. Все предупреждения о допустимости пустых значений отключены.
  • safeonly: контекст аннотации, допускающей значение NULL, включен. Контекст предупреждения, допускающего значение NULL, является безопасным. Переменные ссылочного типа не допускают значения NULL. Все предупреждения о допустимости нулевых значений безопасности включены.
  • warnings: контекст аннотации, допускающей значение NULL, отключен. Контекст предупреждения, допускающего значение NULL, включен. Переменные ссылочного типа не обращают внимания. Все предупреждения о допустимости пустых значений включены.
  • safeonlywarnings: контекст аннотации, допускающей значение NULL, отключен. Контекст предупреждения, допускающего значение NULL, является безопасным. Переменные ссылочного типа не обращают внимания. Все предупреждения о допустимости нулевых значений безопасности включены.

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

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

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

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

Написав такую ​​проверку null, вы предотвратите NullReferenceException, но вы переместите NullArgumentException в точку итерации, а не в точку вызова, а это < em> очень сложно отлаживать.

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

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

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

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

Замечание о разыменовании null в небезопасном коде

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

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

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

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

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

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

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

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

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

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

person Community    schedule 11.01.2011
comment
Может быть, это глупый комментарий, но разве первый и лучший способ избежать этой проблемы - это инициализировать объект? Для меня эта ошибка обычно возникает из-за того, что я забыл инициализировать что-то вроде элемента массива. Я думаю, что гораздо реже определять объект как null, а затем ссылаться на него. Может быть, дать возможность решить каждую проблему рядом с описанием. Тем не менее хороший пост. - person JPK; 20.05.2014
comment
Что делать, если нет объекта, а есть возвращаемое значение из метода или свойства? - person John Saunders; 20.05.2014
comment
Пример книги / автора немного странный .... Как это вообще компилируется? Как вообще работает intellisense? Что это я не умею с компьютером ... - person ; 08.09.2014
comment
@Will: помогает ли моя последняя правка? Если нет, то просьба более четко указать, в чем вы видите проблему. - person John Saunders; 08.09.2014
comment
@JohnSaunders О, нет, извините, я имел в виду версию инициализатора объекта. new Book { Author = { Age = 45 } }; Как происходит даже внутренняя инициализация ... Я не могу представить себе ситуацию, когда внутренний init когда-либо работал бы, но он компилируется и intellisense работает ... Если не для структур? - person ; 08.09.2014
comment
Проверка на null в некоторых случаях не помогает - person bmi; 12.04.2021
comment
@bmi В каких случаях не помогает? - person John Saunders; 27.05.2021

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

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

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

Примечание.

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

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

Сообщение Object not set to an instance of Object означает, что вы пытаетесь использовать объект, который не был инициализирован. Это сводится к одному из следующих:

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

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

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

Отображение отладки IDE

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

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

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

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

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

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

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

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

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

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

Для ясности: 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) Class.


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

Работа с базами данных предоставляет много возможностей для NullReference, потому что одновременно может использоваться много объектов (Command, Connection, Transaction, Dataset, DataTable, _61 _....). Примечание. Не имеет значения, какой поставщик данных вы используете - 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 vs 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 также могут быть Nothing. Решение - поочередно проверять всю цепочку или путь объектов:

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 может быть Nothing или элемент 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...


Формы Visual Basic

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, потому что Nothing не имеет свойства .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 не выдает предупреждений, но решение состоит в том, чтобы объявить контейнеры на уровне формы, но инициализировать их в обработчике событий загрузки формы, когда элементы управления < strong> действительно существуют. Это можно сделать в 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 показывает вам ошибку вашего пути

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

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

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

Не используйте пустые блоки Try / Catch - позвольте коду дать сбой, чтобы вы могли: а) определить причину, б) определить местоположение и в) применить правильное средство. Блоки 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 ...

Как и раньше, вы можете проверить значение 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 истинно.


Мои настройки (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.

or

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

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


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

person Community    schedule 05.11.2014

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

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

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

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

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

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

person Community    schedule 28.06.2012
comment
Отличный улов. Однострочный способ избежать: DateTime x = (DateTime) o as DateTime? ?? defaultValue; - person Serge Shultz; 29.06.2015

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

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

SqlConnection connection = null;
connection.Open();

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

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

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

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

person Community    schedule 11.01.2011
comment
Инструмент Resharper JetBrains определит каждое место в вашем коде, которое может привести к ошибке нулевой ссылки. Это неверно. У меня есть решение без этого обнаружения, но код иногда приводит к исключению. Я подозреваю, что иногда это невозможно обнаружить - по крайней мере, ими - когда задействована многопоточность, но я не могу комментировать дальше, потому что я еще не определил местонахождение своей ошибки. - person j riv; 21.01.2018
comment
Но как решить эту проблему, когда исключение NullReferenceException возникает при использовании HttpContext.Current.Responce.Clear (). Это не решается ни одним из вышеперечисленных решений. потому что при создании его объектного объекта HttpContext возникает ошибка. Разрешение перегрузки завершилось неудачно, поскольку доступный «New» не принимает это количество аргументов. - person Sunny Sandeep; 02.02.2018

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

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

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

person Community    schedule 11.01.2011

Примером этого исключения является: Когда вы пытаетесь что-то проверить, это значение null.

Например:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 

Среда выполнения .NET выдаст исключение NullReferenceException, когда вы попытаетесь выполнить действие с чем-то, что не было создано, то есть с приведенным выше кодом.

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

Дополнительная информация находится в C # NullReferenceException и параметре Null.

person Community    schedule 11.01.2011

Обновление C # 8.0, 2019: типы ссылок, допускающие значение NULL

В C # 8.0 представлены ссылочные типы, допускающие значение NULL и ссылочные типы, не допускающие значения NULL. Поэтому необходимо проверять только ссылочные типы, допускающие значение NULL, чтобы избежать NullReferenceException.


Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он выдаст NullReferenceException.

Пример:

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

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

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

Чтобы полностью понять, почему возникает исключение NullReferenceException, важно знать разницу между типы значений и [ссылочные типы] [3].

Итак, если вы имеете дело с типами значений, исключения NullReferenceExceptions не возникают. Хотя вам нужно быть начеку при работе с ссылочными типами!

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

Типы справочников (эти типы необходимо отметить):

  • динамичный
  • объект
  • нить

Типы значений (вы можете просто игнорировать эти):

  • Числовые типы
  • Интегральные типы
  • Типы с плавающей точкой
  • десятичный
  • bool
  • Пользовательские структуры
person Community    schedule 16.05.2013
comment
-1: поскольку вопрос в том, что такое исключение NullReferenceException, типы значений не имеют значения. - person John Saunders; 17.05.2013
comment
@ Джон Сондерс: Я не согласен. Разработчику программного обеспечения действительно важно уметь различать значения и ссылочные типы. иначе люди будут проверять, равны ли целые числа нулю. - person Fabian Bigler; 17.05.2013
comment
Правда, только не в контексте этого вопроса. - person John Saunders; 17.05.2013
comment
Спасибо за подсказку. Я немного улучшил его и добавил пример вверху. Я все еще думаю, что упоминание типов ссылок и значений полезно. - person Fabian Bigler; 17.05.2013
comment
Я думаю, вы не добавили ничего, чего не было в других ответах, поскольку вопрос предполагает ссылочный тип. - person John Saunders; 19.05.2013

Другой случай, когда NullReferenceExceptions может произойти, - это (неправильное) использование as оператор:

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

Здесь Book и Car несовместимые типы; Car нельзя преобразовать / преобразовать в Book. Когда это приведение не удается, as возвращает null. Использование mybook после этого вызывает NullReferenceException.

В общем, вы должны использовать приведение или as следующим образом:

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

ComicBook cb = (ComicBook)specificBook;

Если вы не уверены в типе, но хотите попробовать использовать его как определенный тип, используйте as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}
person Community    schedule 05.03.2013
comment
Это может происходить часто при распаковке переменной. Я обнаружил, что это часто случается в обработчиках событий после того, как я изменил тип элемента пользовательского интерфейса, но забыл обновить код программной части. - person Brendan; 19.02.2014

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

Пример:

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

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

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

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

person Community    schedule 10.12.2012
comment
Как глубоко! Я никогда не считал "нулевую" константу эталонным значением. Так вот как C # абстрагирует NullPointer, да? B / c, как я помню в C ++, NPE может быть вызвано разыменованием неинициализированного указателя (т.е. типа ref в C #), значение по умолчанию которого является адресом, который не выделен этому процессу (во многих случаях это будет 0, особенно в более поздних версиях C ++, которые выполняли автоматическую инициализацию, которая принадлежит ОС - f с ней и die beeotch (или просто поймайте sigkill, с помощью которого ОС атакует ваш процесс)). - person samis; 31.07.2013

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

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


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

Затем либо Start с отладкой (F5) или Attach [ отладчик VS] в запущенный процесс. Иногда может быть полезно использовать Debugger.Break, после чего будет предложено запустить отладчик.

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

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

var x = myString.Trim();

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

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

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

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


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

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

person Community    schedule 12.02.2014

Саймон Мурье привел этот пример:

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

где распаковка преобразование (приведение) из object (или из одного из классов System.ValueType или System.Enum, или из типа интерфейса) to тип значения (отличный от Nullable<>) сам по себе дает NullReferenceException.

В другом направлении, упаковка преобразование из Nullable<>, у которого HasValue равно false в ссылочный тип, может дать null ссылку, которая позже может привести к NullReferenceException. Классический пример:

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

Иногда бокс бывает по-другому. Например, с этим неуниверсальным методом расширения:

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

следующий код будет проблематичным:

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

Эти случаи возникают из-за особых правил, которые среда выполнения использует при упаковке Nullable<> экземпляров.

person Community    schedule 10.07.2014

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

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

Затем следующий код вызовет исключение 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;}
}

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

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

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

person Community    schedule 18.12.2013

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

См. "NullReferenceException, созданный при тестировании настраиваемого атрибута авторизации" для получения более подробного примера.

person Community    schedule 13.10.2013

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

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

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();
    }
}
person Community    schedule 06.03.2014
comment
-1: это обрабатывает только один сценарий - сценарий неинициализированных зависимостей. Это сценарий меньшинства для NullReferenceException. В большинстве случаев это просто неправильное понимание того, как работают объекты. Далее по частоте следуют другие ситуации, когда разработчик предполагал, что объект будет инициализирован автоматически. - person John Saunders; 07.03.2014
comment
Внедрение зависимостей обычно не используется, чтобы избежать исключения NullReferenceException. Не думаю, что вы нашли здесь общий сценарий. В любом случае, если вы отредактируете свой ответ, сделав его более похожим на stackoverflow.com/a/15232518/76337, тогда я уберу голос против. - person John Saunders; 07.03.2014

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

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

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

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

public X { get; set; }

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

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

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

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

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

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

person Community    schedule 25.12.2014
comment
Я подумал добавить это, поскольку никто об этом не упомянул, и, поскольку он существует как подход, моим намерением было обогатить тему. - person Nick Louloudakis; 26.12.2014
comment
Спасибо за то, что обогатили тему. Я высказал свое мнение о вашем добавлении. Теперь другие могут делать то же самое. - person John Saunders; 26.12.2014
comment
Я подумал, что это стоящее дополнение к теме, учитывая, что это очень просматриваемая ветка. Я слышал о контрактах кода раньше, и это было хорошим напоминанием о возможности их использования. - person VoteCoffee; 08.01.2015

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 
    
person Community    schedule 18.02.2014
comment
Это неверно. String.Empty.ToLower() не вызовет исключение нулевой ссылки. Он представляет собой фактическую строку, хотя и пустую (например, ""). Поскольку у этого есть объект для вызова ToLower(), не имеет смысла создавать исключение с нулевой ссылкой. - person Kjartan; 24.07.2015

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 неправильно интерпретировала амперсанды и скобки.

person Community    schedule 24.07.2015
comment
Вы хотели Html.Partial, а не @Html.Partial - person John Saunders; 24.07.2015
comment
Также покажите, в какой строке возникло исключение и почему. - person John Saunders; 24.07.2015
comment
Ошибка произошла в MyOtherView.cshtml, которую я здесь не включил, потому что модель не отправлялась должным образом (это было Null), поэтому я знал, что ошибка связана с тем, как я отправлял модель. - person Travis Heeter; 27.07.2015

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

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

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

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

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

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

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

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

Есть "Microsoft Code Contracts", где вы используете синтаксис типа 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);
}

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

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

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

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

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

Встроен, как Nullable?

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

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

person Community    schedule 06.03.2016
comment
Никогда не бросайте NullReferenceException - person John Saunders; 06.03.2016
comment
@JohnSaunders смею спросить, почему? (Серьезно, почему?) - person Luis Perez; 07.03.2016
comment
NullReferenceException должно генерироваться средой CLR. Это означает, что произошла ссылка на нуль. Это не означает, что может произойти ссылка на нуль, за исключением того, что вы сначала ловко проверили. - person John Saunders; 07.03.2016
comment
Я понимаю вашу точку зрения о том, насколько это сбивает с толку. Я обновил его до обычного исключения для этого примера и настраиваемого исключения в GitHub. - person Luis Perez; 07.03.2016
comment
Отличный ответ на такой простой вопрос. Это не так уж плохо, когда выходит из строя ваш код. Ужасно, когда он исходит из глубины какой-то коммерческой сторонней библиотеки, на которую вы полагаетесь, а служба поддержки продолжает настаивать на том, что проблема должна быть в вашем коде. И вы не совсем уверены, что это не так, и весь проект должен быть остановлен ... Я действительно думаю, что это могло бы стать подходящей эпитафией для моего надгробия: Ссылка на объект не установлена ​​на экземпляр объекта. - person Darrel Lee; 03.05.2016

Вы можете исправить исключение NullReferenceException чистым способом, используя операторы Null-conditional в C # 6, и написать меньше кода для обработки нулевых проверок.

Он используется для проверки нулевого значения перед выполнением операции доступа к члену (?.) Или индексации (? [).

Пример

  var name = p?.Spouse?.FirstName;

Это эквивалентно:

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }

В результате имя будет нулевым, когда p равно нулю или когда p.Spouse равно нулю.

В противном случае имени переменной будет присвоено значение p.Spouse.FirstName.

Для дополнительных подробностей: Нулевые условные операторы

person Community    schedule 29.11.2017

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

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

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

Пограничный случай # 2: небезопасный код

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

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

Пограничный случай № 3: установка нескольких мониторов Visual Studio с дополнительными мониторами, у которых настройки DPI отличаются от настроек основного монитора.

Этот пограничный случай зависит от программного обеспечения и относится к среде IDE Visual Studio 2019 (и, возможно, более ранние версии).

Способ воспроизведения проблемы: перетащите любой компонент из Панели инструментов в форму Windows на неосновном мониторе с настройкой DPI, отличной от основного монитора, и вы получите всплывающее окно с сообщением «Ссылка на объект не установлена ​​на экземпляр объект." Согласно в этой ветке, эта проблема известна уже довольно давно, и на момент написания она до сих пор не исправлена.

person Community    schedule 22.03.2017
comment
Пример вашего словаря не является крайним случаем. Если объект не является потокобезопасным, то его использование из нескольких потоков дает случайные результаты. Чем ваш пример небезопасного кода отличается от null? - person John Saunders; 24.03.2017

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

Например: допустим, у вас есть класс 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 an error
     }
}

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

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

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

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

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

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.

person Community    schedule 28.07.2016

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

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

Итак, как с этим бороться:

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

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

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

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

person Community    schedule 24.12.2015
comment
Если у вас есть ссылка на объект, GC никогда не очищает его. - person John Saunders; 24.12.2015
comment
если вы используете такие вещи, как FindObject (имя объекта), GC никак не узнает заранее, что вы собираетесь ссылаться на этот объект ... это то, что пытался объяснить ... это происходит во время выполнения - person Akash Gutha; 24.12.2015
comment
Есть несколько фреймворков, которые предоставляют эту функциональность в C #, например, Unity. вопрос не имеет отношения к BCl. Поищите в Интернете, прежде чем критиковать, есть масса подобных функций, и для получения полезной информации я даже использую ее ежедневно. А теперь скажите мне, почему ответ не имеет никакого смысла. - person Akash Gutha; 24.12.2015
comment
docs.unity3d.com/ScriptReference/ проверьте ссылку и поправьте себя, мистер эксперт: p - person Akash Gutha; 24.12.2015
comment
Примеры, которые я видел в вашей ссылке, назначают результаты GameObject.Find полю участника. Это ссылка, и GC не будет собирать ее, пока не будет собран содержащий объект. - person John Saunders; 25.05.2016
comment
В единстве есть концепция сцен ... Итак, если вы измените сцену, все в текущей сцене будет собрано GC. Но сценарии, которые вы пишете, в проекте все время одинаковы, и неправильная ссылка на эти объекты вызовет эту проблему. - person Akash Gutha; 27.05.2016
comment
Unity построен на основе .NET. Нет никакой магии. Когда вы переключаете сцену, вы, вероятно, очищаете структуры данных, которые позволяют ссылаться на элементы сцены по имени. - person John Saunders; 27.05.2016

Буквально самый простой способ исправить исключение NullReferenceExeption состоит из двух способов.

Если у вас есть GameObject, например, с прикрепленным скриптом и переменной с именем rb (жесткое тело), ​​эта переменная будет начинаться с нуля при запуске игры. Вот почему вы получаете исключение NullReferenceExeption, потому что на компьютере нет данных, хранящихся в этой переменной.

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

  1. Добавьте RigidBody к вашему объекту с помощью AddComponent ›Physics› Rigidbody. Затем войдите в свой скрипт и введите rb = GetComponent<Rigidbody>();. Эта строка кода лучше всего работает с вашими функциями Start() или Awake().
  2. Вы можете программно добавить компонент и одновременно назначить переменную с помощью одной строчки кода: rb = AddComponent<RigidBody>();

Дополнительные примечания: если вы хотите, чтобы Unity добавлял компонент к вашему объекту, вы могли бы забыли добавить один, вы можете ввести [RequireComponent(typeof(RigidBody))] над объявлением класса (пробел под всеми вашими using).

Наслаждайтесь и получайте удовольствие от создания игр!

person Community    schedule 14.11.2017

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

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

person Community    schedule 14.10.2017

По сути, это исключение для нулевой ссылки. Как утверждает Microsoft:

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

Что это обозначает?

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

«Эй, подождите, у этого участника нет значений, поэтому он не может выполнить задачу, которую вы ему передаете».

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

NullReferenceException не произойдет, если мы используем члены типа Value.

class Program
{
    static void Main(string[] args)
    {
        string str = null;
        Console.WriteLine(str.Length);
        Console.ReadLine();
    }
}

В приведенном выше коде показана простая строка, которой присвоено значение null.

Теперь, когда я пытаюсь напечатать длину строки str, я получаю сообщение Необработанное исключение типа 'System.NullReferenceException', потому что член str < / strong> указывает на нуль, и не может быть никакой длины нуль.

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

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

class Program
{
    static void Main(string[] args)
    {
        MyClass1 obj;
        obj.foo();  // Use of unassigned local variable 'obj'
    }
}

public class MyClass1
{
    internal void foo()
    {
        Console.WriteLine("Hello from foo");
    }
}

Компилятор для приведенного выше кода вызывает ошибку, что переменная obj не назначена, что означает, что наша переменная имеет нулевые значения или ничего. Компилятор для приведенного выше кода вызывает ошибку, что переменная obj не назначена, что означает, что наша переменная имеет нулевые значения или ничего.

Почему это происходит?

  • Исключение NullReferenceException возникает из-за того, что мы не проверили значение объекта. Мы часто оставляем значения объектов неотмеченными при разработке кода.

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

Как этого избежать?

Есть несколько способов и способов избежать этого известного исключения:

  1. Явная проверка: мы должны придерживаться традиции проверки объектов, свойств, методов, массивов и коллекций на то, являются ли они нулевыми. Это можно просто реализовать с помощью условных операторов, таких как if-else if-else и т. Д.

  2. Обработка исключений: один из важных способов управления этим исключением. Используя простые блоки try-catch-finally, мы можем контролировать это исключение, а также вести его журнал. Это может быть очень полезно, когда ваше приложение находится на стадии разработки.

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

  4. Отладчик: для разработчиков у нас есть большое оружие отладки. Если мы сталкиваемся с NullReferenceException во время разработки, мы можем использовать отладчик, чтобы добраться до источника исключения.

  5. Встроенный метод: системные методы, такие как GetValueOrDefault (), IsNullOrWhiteSpace () и IsNullorEmpty (), проверяют наличие значений NULL и присваивают значение по умолчанию, если имеется значение NULL.

Здесь уже есть много хороших ответов. Вы также можете проверить более подробное описание с примерами на моем блог.

Надеюсь, это тоже поможет!

person Community    schedule 18.07.2017