Что такое NullReferenceException и как я могу это исправить?

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

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

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{amp}lt;Book{amp}gt; Books { get; set; } } public class Book { public string Title { get; set; } } 

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

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

Это переводится как

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

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

 int[] numbers = null; int n = numbers[0]; // numbers is null. There is no array to index. 
 Person[] people = new Person[5]; people[0].Age = 20 // people[0] is null. The array was allocated but not // initialized. There is no Person to set the Age for. 
 long[][] array = new long[1][]; array[0][0] = 3; // is null because only the first dimension is yet initialized. // Use array[0] = new long[2]; first. 
 Dictionary{amp}lt;string, int{amp}gt; 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{amp}lt;Person{amp}gt;(); 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 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(); 

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

Например :

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

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

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

 var myThing = someObject as Thing; 

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

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

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

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

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

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

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

 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() , вы можете использовать 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 во время выполнения в режиме выпуска.

 DateTime? appointment = null; Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now)); // Will display the default value provided (DateTime.Now), because appointment is null. appointment = new DateTime(2022, 10, 20); Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now)); // Will display the appointment date, not the default 

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

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

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

 var title = person.Title.ToUpper(); 

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

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

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

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

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

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

Конечно, вам все равно нужно проверить title на null или использовать оператор условия null вместе с оператором объединения нулей ( ?? ), чтобы задать значение по умолчанию:

 // regular null check int titleLength = 0; if (title != null) titleLength = title.Length; // If title is null, this would throw NullReferenceException // combining the `?` and the `??` operator int titleLength = title?.Length ?? 0; 

Аналогично, для массивов вы можете использовать ?[i] следующим образом:

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

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

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

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

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

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

Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector