У меня есть код, и когда он выполняется, он выдает NullReferenceException
, говоря:
В экземпляре объекта не задана ссылка на объект.
Что это означает, и что я могу сделать, чтобы исправить эту ошибку?
У меня есть код, и когда он выполняется, он выдает NullReferenceException
, говоря:
В экземпляре объекта не задана ссылка на объект.
Что это означает, и что я могу сделать, чтобы исправить эту ошибку?
Вы пытаетесь использовать что-то null
(или Nothing
в VB.NET). Это означает, что вы либо устанавливаете его на null
, либо никогда не устанавливаете его вообще ни на что.
Как и все остальное, null
передают. Если это null
в методе A, возможно, метод B передал null
методу A.
null
может иметь разные значения:
NullReferenceException
.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 и почему он не установлен должным образом.
Некоторые распространенные сценарии, в которых может быть создано исключение:
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.
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;
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!";
}
}
// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();
Если исключение возникает при ссылке на свойство @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
элементы управления создаются во время вызова 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
сам по себе равен нулю). Так что знайте об этом.
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
, пустые контексты и ссылочные типы, допускающие значение 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
к объектам, вы увидите, что этот метод используется повсюду. Его писать немного неудобнее, но он значительно упрощает отладку ошибок, связанных с нулевым значением. Оптимизируйте код для удобства вызывающего абонента, а не для удобства автора.
C#
имеет небезопасный режим, который, как следует из названия, чрезвычайно опасен, поскольку обычные механизмы безопасности, обеспечивающие безопасность памяти и безопасность типов, не применяются. Не следует писать небезопасный код, если у вас нет полного и глубокого понимания того, как работает память.
В небезопасном режиме вы должны знать два важных факта:
Чтобы понять, почему это так, полезно понять, как .NET вообще производит NullReferenceException
. (Эти сведения относятся к .NET, работающему в Windows; другие операционные системы используют аналогичные механизмы.)
Память виртуализирована в Windows
; каждый процесс получает пространство виртуальной памяти из многих страниц памяти, которые отслеживаются операционной системой. На каждой странице памяти установлены флаги, которые определяют, как ее можно использовать: чтение, запись, выполнение и т. Д. Самая низкая страница помечается как вызывающая ошибку, если когда-либо использовалась каким-либо образом.
И нулевой указатель, и нулевая ссылка в C#
внутренне представлены как число ноль, и поэтому любая попытка разыменовать его в соответствующее хранилище памяти вызывает ошибку операционной системы. Затем среда выполнения .NET обнаруживает эту ошибку и превращает ее в NullReferenceException
.
Вот почему разыменование как нулевого указателя, так и нулевой ссылки вызывает одно и то же исключение.
А как насчет второго пункта? Разыменование любого недопустимого указателя, который попадает на самую нижнюю страницу виртуальной памяти, вызывает ту же ошибку операционной системы и, следовательно, такое же исключение.
Почему это имеет смысл? Что ж, предположим, что у нас есть структура, содержащая два целых числа и неуправляемый указатель, равный нулю. Если мы попытаемся разыменовать второй int в структуре, CLR
не будет пытаться получить доступ к хранилищу в нулевом местоположении; он получит доступ к хранилищу в четвертом месте. Но логически это нулевое разыменование, потому что мы попадаем на этот адрес через нулевое значение.
Если вы работаете с небезопасным кодом и получаете NullReferenceException
, просто имейте в виду, что указатель-нарушитель не обязательно должен быть нулевым. Это может быть любое место на самой нижней странице, и будет создано это исключение.
new Book { Author = { Age = 45 } };
Как происходит даже внутренняя инициализация ... Я не могу представить себе ситуацию, когда внутренний init когда-либо работал бы, но он компилируется и intellisense работает ... Если не для структур?
- person ; 08.09.2014
NullReference Exception
для Visual Basic не отличается от такового в C #. В конце концов, они оба сообщают об одном и том же исключении, определенном в .NET Framework, которое они оба используют. Причины, уникальные для Visual Basic, редки (возможно, только одна).
В этом ответе будут использоваться термины, синтаксис и контекст Visual Basic. Используемые примеры взяты из большого количества прошлых вопросов о переполнении стека. Это сделано для максимальной релевантности за счет использования видов ситуаций, которые часто встречаются в сообщениях. Также дается немного больше объяснений для тех, кому это может понадобиться. Пример, похожий на ваш, очень, вероятно, указан здесь.
Примечание.
NullReferenceException
(NRE), как его найти, как исправить и как этого избежать. NRE может быть вызвано многими способами, поэтому вряд ли это будет ваша единственная встреча.Сообщение Object not set to an instance of Object означает, что вы пытаетесь использовать объект, который не был инициализирован. Это сводится к одному из следующих:
Поскольку проблема заключается в ссылке на объект, которая равна Nothing
, ответ состоит в том, чтобы изучить их, чтобы выяснить, какой именно. Затем определите, почему он не инициализирован. Наведите указатель мыши на различные переменные, и Visual Studio (VS) покажет их значения - виновником будет Nothing
.
Вам также следует удалить любые блоки 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 _ strong > - конструктор - где ваш объект может выполнять любую другую инициализацию.
Для ясности: 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 предупреждает, что это может произойти:
Когда он объявлен как переменная уровня модуля / класса, как, похоже, в случае с 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...
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
. Как результат:
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.
Теперь, когда вы знаете, что ищете, их должно быть легко найти:
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 не предназначены для сокрытия исключений от лица, обладающего уникальной квалификацией для их исправления - разработчика.
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 ...
У 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
xlWorkSheet = xlWorkBook.Sheets("sheet1")
For i = 0 To myDGV.RowCount - 1
For j = 0 To myDGV.ColumnCount - 1
For k As Integer = 1 To myDGV.Columns.Count
xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
Next
Next
Next
Когда ваш DataGridView
имеет AllowUserToAddRows
как True
(по умолчанию), Cells
в пустой / новой строке внизу будет содержать Nothing
. Большинство попыток использовать содержимое (например, ToString
) приводят к NRE.
Средство правовой защиты
Используйте цикл For/Each
и проверьте свойство IsNewRow
, чтобы определить, является ли это последней строкой. Это работает независимо от того, верно AllowUserToAddRows
или нет:
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
Если вы действительно используете цикл For n
, измените количество строк или используйте Exit For
, когда IsNewRow
истинно.
При определенных обстоятельствах попытка использовать элемент из My.Settings
, который является StringCollection
, может привести к 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
(всегда).
Другой сценарий - преобразование нулевого объекта в тип значения. Например, код ниже:
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 коде, который отлично компилируется ...
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
}
Это означает, что рассматриваемая переменная ни на что не указывает. Я мог бы сгенерировать это так:
SqlConnection connection = null;
connection.Open();
Это вызовет ошибку, потому что, хотя я объявил переменную connection
, она ни на что не указывает. Когда я пытаюсь вызвать член Open
, для него нет ссылки, и он выдаст ошибку.
Чтобы избежать этой ошибки:
object == null
.Инструмент JetBrains ReSharper определит каждое место в вашем коде, которое может иметь значение null справочная ошибка, позволяющая поставить нулевую проверку. Эта ошибка - источник ошибок номер один, ИМХО.
Имейте в виду, что независимо от сценария причина всегда одна и та же в .NET:
Вы пытаетесь использовать ссылочную переменную со значением _1 _ / _ 2_. Когда значение для ссылочной переменной равно _3 _ / _ 4_, это означает, что на самом деле она не содержит ссылки на экземпляр какого-либо объекта, существующего в куче.
Вы либо никогда ничего не присваивали переменной, никогда не создавали экземпляр значения, присвоенного переменной, либо вы вручную устанавливали переменную равной _5 _ / _ 6_, либо вы вызывали функцию, которая устанавливает для переменной значение _7 _ / _ 8_.
Примером этого исключения является: Когда вы пытаетесь что-то проверить, это значение 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.
Обновление 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 не возникают. Хотя вам нужно быть начеку при работе с ссылочными типами!
Только ссылочные типы, как следует из названия, могут содержать ссылки или указывать буквально ни на что (или «ноль»). В то время как типы значений всегда содержат значение.
Типы справочников (эти типы необходимо отметить):
Типы значений (вы можете просто игнорировать эти):
Другой случай, когда 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) {
// ...
}
Вы используете объект, содержащий ссылку на нулевое значение. Таким образом, это дает нулевое исключение. В этом примере строковое значение равно null, и при проверке его длины возникло исключение.
Пример:
string value = null;
if (value.Length == 0) // <-- Causes exception
{
Console.WriteLine(value); // <-- Never reached
}
Ошибка исключения:
Необработанное исключение:
System.NullReferenceException: ссылка на объект не установлена на экземпляр объекта. в Program.Main ()
Хотя 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.
Саймон Мурье привел этот пример:
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<>
экземпляров.
Добавление случая, когда имя класса для сущности, используемой в структуре сущностей, совпадает с именем класса для файла кода программной части веб-формы.
Предположим, у вас есть веб-форма 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 ()
Другой общий случай, когда можно получить это исключение, связан с фиктивными классами во время модульного тестирования. Независимо от используемого фреймворка имитации вы должны убедиться, что все соответствующие уровни иерархии классов должным образом имитируются. В частности, все свойства HttpContext
, на которые ссылается тестируемый код, должны быть имитированы.
См. "NullReferenceException, созданный при тестировании настраиваемого атрибута авторизации" для получения более подробного примера.
У меня другой взгляд на то, чтобы ответить на этот вопрос. Такого рода ответы "что еще я могу сделать, чтобы этого избежать?"
При работе на разных уровнях, например в приложении 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();
}
}
На вопрос что мне с этим делать может быть много ответов.
Более формальный способ предотвращения таких ошибок при разработке - это применение дизайна по контракту в вашем коде. Это означает, что вам нужно установить инварианты и / или даже функцию / метод предварительные условия и постусловия в вашей системе во время разработки.
Короче говоря, инварианты класса гарантируют, что в вашем классе будут некоторые ограничения, которые не будут нарушены при нормальном использовании (и, следовательно, класс не перейдет в несогласованное состояние ). Предварительные условия означают, что данные, передаваемые в качестве входных данных для функции / метода, должны соответствовать некоторым установленным ограничениям и никогда их не нарушать, а постусловия означают, что функция / вывод метода должен снова следовать установленным ограничениям, не нарушая их. Условия контракта никогда не должны нарушаться во время выполнения программы без ошибок, поэтому проект по контракту проверяется на практике в режиме отладки, а в выпусках отключен, чтобы максимизировать развитая система производительности.
Таким образом, вы можете избежать 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.
В качестве альтернативы можно применить проект по контракту с помощью утверждений em >.
ОБНОВЛЕНИЕ: стоит упомянуть, что термин был придуман Бертраном Мейером в связи с разработкой языка программирования Eiffel.
NullReferenceException
выдается, когда мы пытаемся получить доступ к свойствам нулевого объекта или когда строковое значение становится пустым и мы пытаемся получить доступ к строковым методам.
Например:
При доступе к строковому методу пустой строки:
string str = string.Empty;
str.ToLower(); // throw null reference exception
При доступе к свойству нулевого объекта:
Public Class Person {
public string Name { get; set; }
}
Person objPerson;
objPerson.Name /// throw Null refernce Exception
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 неправильно интерпретировала амперсанды и скобки.
Html.Partial
, а не @Html.Partial
- person John Saunders; 24.07.2015
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 be
null. Therefore you only have to check whether
addressis
null`.
Это отличная функция, но она дает меньше информации. Это не делает очевидным, какой из 4 является нулевым.
Встроен, как Nullable?
В C # есть отличное сокращение для Nullable<T>
, вы можете сделать что-нибудь, допускающее значение NULL, поставив знак вопроса после типа, например int?
.
Было бы неплохо, если бы в C # было что-то вроде структуры NotNull<T>
, приведенной выше, и было бы аналогичное сокращение, возможно, восклицательный знак (!), Чтобы вы могли написать что-то вроде: public void WriteName(Person! person)
.
Вы можете исправить исключение 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.
Для дополнительных подробностей: Нулевые условные операторы
Интересно, что ни один из ответов на этой странице не упоминает два крайних случая:
Универсальные словари в .NET не являются потокобезопасными, и они иногда могут выдавать NullReference
или даже (чаще) KeyNotFoundException
, когда вы пытаетесь получить доступ к ключу из двух параллельных потоков. В этом случае исключение вводит в заблуждение.
Если NullReferenceException
выдает unsafe
код, вы можете посмотреть на свои переменные указателя и проверить их на IntPtr.Zero
или что-то в этом роде. Это то же самое (исключение нулевого указателя), но в небезопасном коде переменные часто приводятся к типам-значениям / массивам и т. Д., И вы бьетесь головой о стену, задаваясь вопросом, как тип-значение может вызывать это исключение.
(Кстати, еще одна причина не использовать небезопасный код, если он вам не нужен.)
Этот пограничный случай зависит от программного обеспечения и относится к среде IDE Visual Studio 2019 (и, возможно, более ранние версии).
Способ воспроизведения проблемы: перетащите любой компонент из Панели инструментов в форму Windows на неосновном мониторе с настройкой DPI, отличной от основного монитора, и вы получите всплывающее окно с сообщением «Ссылка на объект не установлена на экземпляр объект." Согласно в этой ветке, эта проблема известна уже довольно давно, и на момент написания она до сих пор не исправлена.
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;
}
}
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.
Ну простыми словами:
Вы пытаетесь получить доступ к объекту, который не создан или в настоящее время не находится в памяти.
Итак, как с этим бороться:
Выполните отладку и позвольте отладчику перестать работать ... Он напрямую перенесет вас к переменной, которая не работает ... Теперь ваша задача просто исправить это ... Используя ключевое слово new в соответствующем месте.
Если это вызвано некоторыми командами базы данных из-за того, что объект отсутствует, все, что вам нужно сделать, это выполнить нулевую проверку и обработать его:
if (i == null) {
// Handle this
}
Самый сложный ... если GC собрал объект уже ... Это обычно происходит, если вы пытаетесь найти объект, используя строки ... То есть, находя его по имени объекта, тогда может случиться так, что сборщик мусора уже очистил его ... Это сложно find и станет настоящей проблемой ... Лучший способ решить эту проблему - выполнять нулевые проверки везде, где это необходимо, в процессе разработки. Это сэкономит вам много времени.
Под поиском по имени я подразумеваю, что некоторая структура позволяет вам использовать FIndObjects с использованием строк, и код может выглядеть следующим образом: FindObject ("ObjectName");
Буквально самый простой способ исправить исключение NullReferenceExeption состоит из двух способов.
Если у вас есть GameObject, например, с прикрепленным скриптом и переменной с именем rb (жесткое тело), эта переменная будет начинаться с нуля при запуске игры. Вот почему вы получаете исключение NullReferenceExeption, потому что на компьютере нет данных, хранящихся в этой переменной.
В качестве примера я буду использовать переменную RigidBody. Мы можем очень легко добавлять данные несколькими способами:
rb = GetComponent<Rigidbody>();
. Эта строка кода лучше всего работает с вашими функциями Start()
или Awake()
.rb = AddComponent<RigidBody>();
Дополнительные примечания: если вы хотите, чтобы Unity добавлял компонент к вашему объекту, вы могли бы забыли добавить один, вы можете ввести [RequireComponent(typeof(RigidBody))]
над объявлением класса (пробел под всеми вашими using).
Наслаждайтесь и получайте удовольствие от создания игр!
Если это сообщение появляется во время сохранения или компиляции сборки, просто закройте все файлы, а затем откройте любой файл для компиляции и сохранения.
Для меня причина заключалась в том, что я переименовал файл, а старый файл все еще был открыт.
По сути, это исключение для нулевой ссылки. Как утверждает 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 возникает из-за того, что мы не проверили значение объекта. Мы часто оставляем значения объектов неотмеченными при разработке кода.
Это также возникает, когда мы забываем создать экземпляры наших объектов. Причиной этого исключения также может быть использование методов, свойств, коллекций и т. Д., Которые могут возвращать или устанавливать нулевые значения.
Есть несколько способов и способов избежать этого известного исключения:
Явная проверка: мы должны придерживаться традиции проверки объектов, свойств, методов, массивов и коллекций на то, являются ли они нулевыми. Это можно просто реализовать с помощью условных операторов, таких как if-else if-else и т. Д.
Обработка исключений: один из важных способов управления этим исключением. Используя простые блоки try-catch-finally, мы можем контролировать это исключение, а также вести его журнал. Это может быть очень полезно, когда ваше приложение находится на стадии разработки.
Нулевые операторы: Нулевой оператор объединения и нулевые условные операторы также могут быть полезны при установке значений для объектов, переменных, свойств и полей.
Отладчик: для разработчиков у нас есть большое оружие отладки. Если мы сталкиваемся с NullReferenceException во время разработки, мы можем использовать отладчик, чтобы добраться до источника исключения.
Встроенный метод: системные методы, такие как GetValueOrDefault (), IsNullOrWhiteSpace () и IsNullorEmpty (), проверяют наличие значений NULL и присваивают значение по умолчанию, если имеется значение NULL.
Здесь уже есть много хороших ответов. Вы также можете проверить более подробное описание с примерами на моем блог.
Надеюсь, это тоже поможет!