А NullReferenceException
происходит, когда вы пытаетесь получить доступ к ссылочной переменной, которая не ссылается ни на один объект. Если ссылочная переменная не ссылается на объект, она будет рассматриваться как null
. Среда выполнения сообщит вам, что вы пытаетесь получить доступ к объекту, когда переменная равна null
выпустив NullReferenceException
.
Ссылочные переменные в C# и JavaScript по своей концепции аналогичны указателям в C и C++. Типы ссылок по умолчанию null
чтобы указать, что они не ссылаются ни на один объект. Следовательно, если вы попытаетесь получить доступ к объекту, на который ссылаетесь, а его нет, вы получите NullReferenceException
.
Когда получишь NullReferenceException
в вашем коде это означает, что вы забыли установить переменную перед ее использованием. Сообщение об ошибке будет выглядеть примерно так:
NullReferenceException: Object reference not set to an instance of an object
at Example.Start () [0x0000b] in /Unity/projects/nre/Assets/Example.cs:10
В этом сообщении об ошибке говорится, что NullReferenceException
произошло в строке 10 файла сценария Example.cs
. Также в сообщении говорится, что исключение произошло внутри Start()
функция. Это позволяет легко найти и исправить исключение нулевой ссылки. В этом примере код:
//c# example
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
// Use this for initialization
void Start () {
go = GameObject.Find("wibble");
Debug.Log(go.name);
}
}
Код просто ищет игровой объект под названием «wibble». В этом примере нет игрового объекта с таким именем, поэтому Find()
функция возвращает null
. В следующей строке (строка 9) мы используем go
переменную и попробуйте распечатать имя игрового объекта, на который она ссылается. Поскольку мы обращаемся к несуществующему игровому объекту, среда выполнения выдает нам NullReferenceException
Переменные в . NET — это либо ссылочные типы, либо типы значений. Типы значений — это примитивы, такие как целые числа .
и логические значения
или структуры (и могут быть идентифицированы, поскольку они наследуются от System. ValueType
). Логические переменные, когда они объявлены, имеют значение по умолчанию:
bool mybool;
//mybool == false
Ссылочные типы при объявлении не имеют значения по умолчанию:
class ExampleClass
{
}
ExampleClass exampleClass; //== null
Если вы попытаетесь получить доступ к члену экземпляра класса, используя нулевую ссылку, вы получите System. Исключение NullReferenceException
. Это то же самое, что Ссылка на объект не установлена на экземпляр объекта
.
static void Main(string[] args)
{
var exampleClass = new ExampleClass();
var returnedClass = exampleClass.ExampleMethod();
returnedClass.AnotherExampleMethod(); //NullReferenceException here.
}
class ExampleClass
{
public ReturnedClass ExampleMethod()
{
return null;
}
}
class ReturnedClass
{
public void AnotherExampleMethod()
{
}
}
Это очень распространенная ошибка, которая может возникнуть по самым разным причинам. Основная причина действительно зависит от конкретного сценария, с которым вы столкнулись.
static void Main(string[] args)
{
var exampleClass = new ExampleClass();
var returnedClass = exampleClass.ExampleMethod();
if (returnedClass == null)
{
//throw a meaningful exception or give some useful feedback to the user!
return;
}
returnedClass.AnotherExampleMethod();
}
Все вышеперечисленное на самом деле лишь намеки на . NET Type Fundamentals, для получения дополнительной информации я бы рекомендовал либо выбрать CLR через C#
или прочитав эту статью MSDN
того же автора — Джеффри Рихтера. Также обратите внимание на гораздо более сложный пример .
того, когда вы можете столкнуться с NullReferenceException.
Некоторые команды, использующие Resharper, используют атрибуты JetBrains
для аннотирования кода, чтобы выделить места, где ожидаются (не) значения NULL.
- Списки и коллекции
- Резюме
- Органы управления
- DataGridView
- Пример 2. Остерегайтесь NewRow
- Ключевые моменты
- Массивы
- Плохо реализованная попытка/поймать
- Блоки Try/Catch
- Примеры
- Цепочка
- Неявно
- Массив
- Элементы массива
- Массив массивов
- Коллекция/Список/Словарь
- ЛИНК
- События
- Неудачное название категории
- Цикл жизни страницы ASP. НЕТ
- Сессии ASP. ЧИСТАЯ
- Пустые вью-модели ASP. NET MVC
- Объекты поставщиков данных
- Null Checks
- Отладка
- UI Controls
- Способы избежать
- Явно проверять на null , пропускать код
- Явно проверять на null , использовать значение по умолчанию
- Явно проверять на null , выбрасывать своё исключение
- Использовать Debug.Assert для проверки на null для обнаружения ошибки до бросания исключения
- Использовать GetValueOrDefault() для Nullable типов
- Использовать оператор ?? (C#) или If() (VB)
- Использовать операторы ?. и ?[ (C# 6+, VB. NET 14+):
- Visual Basic Forms
- How to fix this error?
- 1) Using Null conditional operators
- 2) Using the Null Coalescing operator
- 3) Using nullable datatypes in C#
- Settings (StringCollection)
- DBNull — это не то же самое, что Nothing
- Вкратце
- Пути к объектам / Вложенные
- Более подробно
- Основное значение
- Объекты класса/Создание экземпляра
- В поисках причины
- Функция, ничего не возвращающая
- Исключение NullReference — Visual Basic
- Ссылка на объект не установлена на экземпляр объекта» ошибка?
Списки и коллекции
. NET-коллекции (которых существует множество разновидностей — Lists, Dictionary и т. д.) также должны быть инстанцированы или созданы.
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
Резюме
-
NullReferenceException
происходит, когда ваш код сценария пытается использовать переменную, которая не установлена (ссылается) и объект. - Появившееся сообщение об ошибке многое расскажет вам о том, в каком месте кода возникает проблема.
-
NullReferenceException
этого можно избежать, написав код, который проверяетnull
перед доступом к объекту или использует блоки try/catch.
Органы управления
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
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
Если вы используете For n
цикл, измените количество строк или используйте Exit For
когда IsNewRow
правда.
Ключевые моменты
Вы, наверное, забыли New
оператор.
Что-то, что, как вы предполагали, будет безупречно работать и возвращать инициализированный объект в ваш код, не оправдалось.
Не игнорируйте предупреждения компилятора (никогда) и используйте Option Strict On
(всегда).
Исключение MSDN NullReference
Массивы
Массивы также должны быть созданы:
Private arr as String()
Этот массив только объявлен, но не создан. Существует несколько способов инициализации массива:
Private arr as String() = New String
{}
' or
Private arr() As String = New String{}
' For a local array (in a procedure) and using 'Option Infer':
Dim arr = New String {}
Примечание. Начиная с 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
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
Плохо реализованная попытка/поймать
Плохо реализованный 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
Результаты.
Блоки Try/Catch
using UnityEngine;
using System;
using System.Collections;
public class Example2 : MonoBehaviour {
public Light myLight; // set in the inspector
void Start () {
try {
myLight.color = Color.yellow;
}
catch (NullReferenceException ex) {
Debug.Log("myLight was not set in the inspector");
}
}
}
Примеры
Несколько примеров, в которых возникают выводы.
Цепочка
ref1.ref2.ref3.member
Если ref1
, ref2
или ref3
равно null
, вы получите NullReferenceException
. Для решения проблем и определения, что именно равно null
, вы можете переписать выражение более простым способом:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
Неявно
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; // Свойство Author не было инициализировано
// нет Person, у которого можно вычислить Age.
}
}
То же верно и для вложенных изоляторов:
Book b1 = new Book { Author = { Age = 45 } };
Несмотря на использование ключевого слова new
, создаётся только экземпляр класса Book
, но экземпляр Person
не создаётся, поэтому свойство Author
остается null
.
Массив
int[] numbers = null;
int n = numbers[0]; // numbers = null. Нет массива, чтобы получить элемент по индексу
Элементы массива
Person[] people = new Person[5];
people[0].Age = 20; // people[0] = null. Массив создаётся, но не
// инициализируется. Нет Person, у которого можно задать Age.
Массив массивов
long[][] array = new long[1][];
array[0][0] = 3; // = null, потому что инициализировано только первое измерение.
// Сначала выполните array[0] = new long[2].
Коллекция/Список/Словарь
Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames = null.
// Экземпляр словаря не создан.
ЛИНК
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(); // Исключение бросается здесь, хотя создаётся
// строкой выше. p = null, потому что
// первый добавленный элемент = null.
События
public class Demo
{
public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e)
{
StateChanged(this, e); // Здесь бросится исключение, если на
// событие StateChanged никто не подписался
}
}
Неудачное название категории
Если бы в коде ниже в локальных запросах и полях были разные имена, вы бы обнаружили, что поле не было обосновано:
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. НЕТ
public partial class Issues_Edit : System.Web.UI.Page
{
protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Выполняется только на первой загрузке, но не когда нажата кнопка
myIssue = new TestIssue();
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
myIssue.Entry = "NullReferenceException здесь!";
}
}
Сессии ASP. ЧИСТАЯ
// Если сессионная переменная "FirstName" ещё не была задана,
// то эта строка бросит NullReferenceException.
string firstName = Session["FirstName"].ToString();
Пустые вью-модели ASP. NET MVC
Если вы возвращаете пустую модель (или модель свойства) в контроллере, то вы бросаете ссылки при открытии доступа к ней:
// Controller
public class Restaurant:Controller
{
public ActionResult Search()
{
return View(); // Модель не задана.
}
}
// Razor view
@foreach (var restaurantSearch in Model.RestaurantSearch) // Исключение.
{
}
Объекты поставщиков данных
Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim MaxRows As Integer
con.Open()
Dim sql = "SELECT * FROM tblfoobar_List"
da = New OleDbDataAdapter(sql, con)
da.Fill(ds, "foobar")
con.Close()
MaxRows = ds.Tables("foobar").Rows.Count ' Error
Как и прежде, ds
Объект набора данных был объявлен, но экземпляр так и не был создан. DataAdapter
заполнит существующий DataSet
, а не создавать его. В этом случае, поскольку ds
— локальная переменная, IDE предупреждает
что это может случиться:
Когда объявлено как переменная уровня модуля/класса, как в случае с con
, компилятор не может знать, был ли объект создан вышестоящей процедурой. Не игнорируйте предупреждения.
Dim ds As New DataSet
ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Employees")
txtID.Text = ds.Tables("Employee").Rows(0).Item
txtID.Name = ds.Tables("Employee").Rows(0).Item
Здесь проблема в опечатке: Employees
против Employee
. Не было DataTable
создано имя «Сотрудник», поэтому NullReferenceException
результаты, пытаясь получить к нему доступ. Another potential problem is assuming there will be Items
which may not be so when the SQL includes a WHERE clause.
Since this uses one table, using Tables(0)
will avoid spelling errors. Examining Rows.Count
can also help:
If ds.Tables(0).Rows.Count > 0 Then
txtID.Text = ds.Tables(0).Rows(0).Item
txtID.Name = ds.Tables(0).Rows(0).ItemItems
End If
Fill
is a function returning the number of Rows
affected which can also be tested:
If da.Fill(ds, "Employees") > 0 Then...
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
The DataAdapter
will provide TableNames
as shown in the previous example, but it does not parse names from the SQL or database table. As a result, ds.Tables("TICKET_RESERVATION")
references a non-existent table.
The Remedy
is the same, reference the table by index:
If ds.Tables(0).Rows.Count > 0 Then
See also DataTable Class
.
Null Checks
Although it can be frustrating when this happens it just means the script needs to be more careful. The solution in this simple example is to change the code like this:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
void Start () {
GameObject go = GameObject.Find("wibble");
if (go) {
Debug.Log(go.name);
} else {
Debug.Log("No game object called wibble found");
}
}
}
Now, before we try and do anything with the go
variable, we check to see that it is not null
. If it is null
, then we display a message.
Отладка
Как определить источник ошибки? Кроме изучения, собственно, исключения, которое будет выброшено именно там, где оно произошло, вы можете воспользоваться общими рекомендациями по отладке в Visual Studio: поставьте точки останова в ключевых точках, изучите значения переменных
, либо расположив курсор мыши над переменной, либо открыв панели для отладки: Watch, Locals, Autos.
Если вы хотите определить место, где значение ссылки устанавливается или не устанавливается, нажмите правой кнопкой на её имени и выберите «Find All References». Затем вы можете поставить точки останова на каждой найденной строке и запустить приложение в режиме отладки. Каждый раз, когда отладчик остановится на точке останова, вы можете удостовериться, что значение верное.
Следя за ходом выполнения программы, вы придёте к месту, где значение ссылки не должно быть null
, и определите, почему не присвоено верное значение.
UI Controls
Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar " _
& "FROM Invoice where invoice_no = '" & _
Me.ComboBox5.SelectedItem.ToString.Trim & "' And category = '" & _
Me.ListBox1.SelectedItem.ToString.Trim & "' And item_name = '" & _
Me.ComboBox2.SelectedValue.ToString.Trim & "' And expiry_date = '" & _
Me.expiry.Text & "'", con)
Validate data before using it (also use Option Strict
and SQL parameters):
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
Способы избежать
Явно проверять на 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); // Может вернуть null
if (book == null)
throw new BookNotFoundException(bookTitle); // Ваше исключение
return book.Category;
}
Использовать Debug.Assert
для проверки на null
для обнаружения ошибки до бросания исключения
Если во время разработки вы знаете, что метод может, но вообще-то не должен возвращать null
, вы можете воспользоваться Debug.Assert
для быстрого обнаружения ошибки:
string GetTitle(int knownBookID) {
// Вы знаете, что метод не должен возвращать null
var book = library.GetBook(knownBookID);
// Исключение будет выброшено сейчас, а не в конце метода.
Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Остальной код...
return book.Title; // Не выбросит NullReferenceException в режиме отладки.
}
Однако эта проверка не будет работать в релизной сборке, и вы снова получите NullReferenceException
, если book == null
.
Использовать GetValueOrDefault()
для Nullable типов
DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Отобразит значение по умолчанию, потому что appointment = null.
appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Отобразит дату, а не значение по умолчанию.
Использовать оператор ??
(C#) или If()
(VB)
Краткая запись для задания значения по умолчанию:
IService CreateService(ILogger log, Int32? frobPowerLevel)
{
var serviceImpl = new MyService(log ?? NullLog.Instance);
serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}
Использовать операторы ?.
и ?[
(C# 6+, VB. NET 14+):
Это оператор безопасного доступа к членам, также известный как оператор Элвиса за специфическую форму. Если выражение слева от оператора равно null
, то правая часть игнорируется, и результатом считается null
. Например:
var title = person.Title.ToUpper();
Если свойство Title
равно null
, то будет брошено исключение, потому что это попытка вызвать метод ToUpper
на значении, равном null
. В C# 5 и ниже можно добавить проверку:
var title = person.Title == null ? null : person.Title.ToUpper();
Теперь вместо бросания исключения переменной title
будет присвоено null
. В C# 6 был добавлен более короткий синтаксис:
var title = person.Title?.ToUpper();
Разумеется, если переменная person
может быть равна null
, то надо проверять и её. Также можно использовать операторы ?.
и ??
вместе, чтобы предоставить значение по умолчанию:
// обычная проверка на null
int titleLength = 0;
if (title != null)
titleLength = title.Length;
// совмещаем операторы `?.` и `??`
int titleLength = title?.Length ?? 0;
Если любой член в цепочке может быть null
, то можно полностью обезопасить себя (хотя, конечно, архитектуру стоит поставить под сомнение):
int firstCustomerOrderCount = customers?[0]?.Orders?.Count() ?? 0;
Visual Basic Forms
Public Class Form1
Private NameBoxes = New TextBox
{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
This is a fairly common way to get an NRE. In C#, depending on how it is coded, the IDE will report that Controls
does not exist in the current context, or «cannot reference non-static member». So, to some extent, this is a VB-only situation. It is also complex because it can result in a failure cascade.
The arrays and collections cannot be initialized this way.
This initialization code will run before
the constructor creates the Form
or the Controls
. As a result:
- Lists and Collection will simply be empty
- The Array will contain five elements of Nothing
- The
somevar
assignment will result in an immediate NRE because Nothing doesn’t have a.Text
property
Referencing array elements later will result in an NRE. If you do this in Form_Load
, due to an odd bug, the IDE may not
report the exception when it happens. The exception will pop up later
when your code tries to use the array. This «silent exception» is detailed in this post
. For our purposes, the key is that when something catastrophic happens while creating a form ( Sub New
or Form Load
event), exceptions may go unreported, the code exits the procedure and just displays the form.
Since no other code in your Sub New
or Form Load
event will run after the NRE, a great many other things
can be left uninitialized.
Sub Form_Load(..._
'...
Dim name As String = NameBoxes?.
.Text ' NRE
' ...
' More code (which will likely not be executed)
' ...
End Sub
Note
this applies to any and all control and component references making these illegal where they are:
Public Class Form1
Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
Private studentName As String = TextBox13.Text
It is curious that VB does not provide a warning, but the remedy is to declare
the containers at the form level, but initialize
them in form load event handler when the controls do
exist. This can be done in Sub New
as long as your code is after the InitializeComponent
call:
' 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
The array code may not be out of the woods yet. Any controls which are in a container control (like a GroupBox
or Panel
) will not be found in Me.Controls
; they will be in the Controls collection of that Panel or GroupBox. Nor will a control be returned when the control name is misspelled ( "TeStBox2"
). In such cases, Nothing
will again be stored in those array elements and an NRE will result when you attempt to reference it.
These should be easy to find now that you know what you are looking for:
«Button2» resides on a Panel
Rather than indirect references by name using the form’s Controls
collection, use the control reference:
' 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...})
How to fix this error?
1) Using Null conditional operators
This method is easier than using an if-else condition to check whether the variable value is null. Look at this example,
int? length = customers?.Length; // this will return null if customers is null, instead of throwing the exception
2) Using the Null Coalescing operator
This operator looks like “??”
and provides a default value to variables that have a null value. It is compatible with all nullable datatypes
.
int length = customers?.Length ?? 0; // 0 is provided by default if customers is null
3) Using nullable datatypes in C#
All reference types in C# can have a null value
. But some data types such as int
and Boolean
cannot take null values unless they are explicitly defined. This is done by using Nullable
data types.
static int Add(string roll_numbers)
{
return roll_numbers.Split(","); // This code might throw a NullReferenceException as roll_numbers variable can be null
}
static int Add(string? roll_numbers) // As roll_numbers argument can now be null, the NullReferenceException can be avoided
{
return roll_numbers.Split(",");
}
The best way to avoid the «NullReferenceException: Object reference not set to an instance of an object”
error is to check the values of all variables while coding. You can also use a simple if-else statement to check for null values, such as if (numbers!=null)
to avoid this exception.
Settings (StringCollection)
Under certain circumstances, trying to use an item from My.Settings
which is a StringCollection
can result in a NullReference the first time you use it. The solution is the same, but not as obvious. Рассмотрим:
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
DBNull — это не то же самое, что Nothing
For Each row As DataGridViewRow In dgvPlanning.Rows
If Not IsDBNull(row.Cells(0).Value) Then
...
IsDBNull
функция используется для проверки того, имеет ли значение
равно System.DBNull
: Из MSDN:
Система. Значение DBNull указывает, что объект представляет отсутствующие или несуществующие данные. D BNull — это не то же самое, что Nothing, что указывает на то, что переменная еще не инициализирована.
If row.Cells(0) IsNot Nothing Then ...
Как и раньше, вы можете проверить ничего, а затем конкретное значение:
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
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...
Вкратце
Вы пытаетесь воспользоваться чем-то, что равно null
(или Nothing
в ВБ. СЕТЬ). Это означает, что либо вы присвоили это значение, либо вы ничего не присвоили.
Как и любое другое значение, null
может передаваться от объекта к объекту, от метода к методу. Если нечто равно null
в методе «А» вполне может быть, что метод «В» передал это значение в методе «А».
Остальная часть статьи описывает происходящее в деталях и перечисляет распространённые ошибки, которые могут привести к исключению NullReferenceException
.
Пути к объектам / Вложенные
If myFoo.Bar.Items IsNot Nothing Then
...
Код только тестируется Items
в то время как оба myFoo
и Bar
также может быть Ничто. Средство
заключается в проверке всей цепочки или пути объектов по одному:
If (myFoo IsNot Nothing) AndAlso
(myFoo.Bar IsNot Nothing) AndAlso
(myFoo.Bar.Items IsNot Nothing) Then
....
AndAlso
это важно. Последующие тесты не будут проводиться после первого False
.
встречается условие. Это позволяет коду безопасно «детализировать» объект(ы) по одному «уровнем» за раз, оценивая myFoo.Bar
только после (и если) myFoo
определяется как действительный. Цепочки объектов или пути могут оказаться довольно длинными при кодировании сложных объектов:
myBase.myNodes.Layer.SubLayer.Foo.Files.Add("somefilename")
Невозможно ссылаться на что-либо «ниже по течению» от null
объект. Это касается и контролей:
myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
Вот, myWebBrowser
или Document
может быть Ничего или formfld1
элемент может не существовать.
Более подробно
Если соблюдение требований по охране окружающей среды выбрасывает ограничения NullReferenceException
, то это всегда
означает одно: вы пытаетесь воспользоваться ссылкой. И эта ссылка не инициализирована (или была
изобретена, но уже
не идеяизирована).
Это означает, что ссылка равна null
, а вы не смогли вызвать методы через ссылку, равную null
. В простейшем случае:
string foo = null;
foo.ToUpper();
Этот код выброса содержит NullReferenceException
во второй строке, потому что вы не можете вызвать метод ToUpper()
по ссылкам на string
, равной null
.
Основное значение
Сообщение «Объект не установлен в экземпляр Object»
означает, что вы пытаетесь использовать объект, который не был инициализирован. Это сводится к одному из них:
- Ваш код объявлен
объектная переменная, но она не инициализировалась
это (создать экземпляр или ' создать экземпляр
' это) - Что-то, что, по вашему коду, должно было инициализировать объект, не произошло
- Возможно, другой код преждевременно сделал недействительным всё ещё используемый объект
Объекты класса/Создание экземпляра
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
оператор является причиной №1NullReference Exceptions
видно в рассмотренных вопросах о переполнении стека.Visual Basic пытается неоднократно прояснить процесс, используя
New
: ИспользуяNew
Оператор создает новый
объект и звонкиSub New
-- конструктор -- где ваш объект может выполнять любую другую инициализацию.
В поисках причины
Поскольку проблема заключается в ссылке на объект, которая Nothing
, ответ состоит в том, чтобы изучить их, чтобы выяснить, какой из них. Затем определите, почему он не инициализируется. Удерживайте указатель мыши над различными переменными, и Visual Studio (VS) покажет их значения — виновником будет Nothing
.
Вы также можете использовать Locals Window
( Отладка -> Windows -> Локальные
) для изучения ваших объектов.
Как только вы узнаете, в чем и где проблема, ее обычно довольно легко исправить, и это быстрее, чем задавать новый вопрос.
Функция, ничего не возвращающая
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...
Исключение NullReference — Visual Basic
NullReference Exception
для Visual Basic
ничем не отличается от такового в C#
. В конце концов, они оба сообщают об одном и том же исключении, определенном в файле . NET Framework, который они оба используют. Причины, уникальные для Visual Basic, встречаются редко (возможно, только одна).
- Это основано на концепции: вам не нужно вставлять код в свой проект. Он призван помочь вам понять, что вызывает
NullReferenceException
(NRE), как его найти, как исправить и как этого избежать. NRE может быть вызван разными способами, поэтому вряд ли это будет ваша единственная встреча. - Примеры (из постов Stack Overflow) не всегда показывают лучший способ сделать что-то.
- Обычно применяют самое простое средство.
Ссылка на объект не установлена на экземпляр объекта» ошибка?
Как упоминалось ранее, NullReferenceException
указывает, что ваш код пытается работать с объектом, ссылка на который имеет нулевое значение. Это означает, что ссылочный объект не был инициализирован.
try
{
string a = null;
a.ToString();
}
catch (NullReferenceException e)
{
//Code to do something with e
}