Foreversoft.ru

IT Справочник
1 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Ошибка system nullreferenceexception

Что такое NullReferenceException, и как мне исправить код?

Когда я выполняю некоторый код, выбрасывается исключение NullReferenceException со следующим сообщением:

Object reference not set to an instance of an object.

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

Что это значит, и как мне исправить код?

2 ответа 2

Причина

Вкратце

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

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

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

Более подробно

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

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

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

Отладка

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

Если вы хотите определить место, где значение ссылки устанавливается или не устанавливается, нажмите правой кнопкой на её имени и выберите «Find All References». Затем вы можете поставить точки останова на каждой найденной строке и запустить приложение в режиме отладки. Каждый раз, когда отладкик остановится на точке останова, вы можете удостовериться, что значение верное.

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

Примеры

Несколько общих примеров, в которых возникает исключение.

Цепочка

Если ref1 , ref2 или ref3 равно null , вы получите NullReferenceException . Для решения проблемы и определения, что именно равно null , вы можете переписать выражение более простым способом:

Например, в цепочке HttpContext.Current.User.Identity.Name , значение может отсутствовать и у HttpContext.Current , и у User , и у Identity .

Неявно

То же верно для вложенных инициализаторов:

Несмотря на использование ключевого слова new , создаётся только экземпляр класса Book , но экземпляр Person не создаётся, поэтому свойство Author остаётся null .

Массив

Элементы массива

Массив массивов

Collection/List/Dictionary

События

Неудачное именование переменных

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

Можно избежать проблемы, если использовать префикс для полей:

Цикл жизни страницы ASP.NET

Сессии ASP.NET

Пустые вью-модели ASP.NET MVC

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

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

Явно проверять на null , пропускать код

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

Явно проверять на null , использовать значение по умолчанию

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

Явно проверять на null , выбрасывать своё исключение

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

Использовать Debug.Assert для проверки на null для обнаружения ошибки до бросания исключения

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

Однако эта проверка не будет работать в релизной сборке, и вы снова получите NullReferenceException , если book == null .

Использовать GetValueOrDefault() для Nullable типов

Использовать оператор ?? (C#) или If() (VB)

Краткая запись для задания значения по умолчанию:

Использовать операторы ?. и ?[ (C# 6+, VB.NET 14+):

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

Если свойство Title равно null , то будет брошено исключение, потому что это попытка вызвать метод ToUpper на значении, равном null . В C# 5 и ниже можно добавить проверку:

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

Читать еще:  Абсолютный и относительный путь к файлу linux

Разумеется, если переменная person может быть равна null , то надо проверять и её. Также можно использовать операторы ?. и ?? вместе, чтобы предоставить значение по умолчанию:

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

В дополнение к ответу @Discord @Squidward @Athari @Kyubey, давайте рассмотрим вопрос с другой стороны.

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

а имеет ли право эта ссылка иметь значение null ?

Во многих случаях правильным ответом будет «нет», и значит, исправлять придётся истинную причину ошибки, которая находится в другом месте, и произошла раньше.

Пример: если у вас есть такой класс:

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

Поэтому если вы видите, что обращение к engine.HorsePower падает с NullReferenceException , реальная проблема состоит в том, что вы забыли инициализировать engine в конструкторе. Поэтому и исправлять ошибку нужно не в точке, где падает, а в том месте, которое реально должно бы обеспечить ненулевое значение engine .

А вот если вылетает обращение driver.Age , то здесь уже проблема прямо в точке обращения, вам необходимо сначала проверить, что driver != null , а потом уж обращаться.

Таким образом: если ваша ссылка в точке обращения не имеет права иметь значение null , то вы не должны дописывать проверку на null , тем самым «замазывая» ошибку. Вы должны либо ничего не проверять, а исправить в том месте, где ссылка должна быть инициализирована, либо добавить Debug.Assert , либо проверку на null и выброс исключения.

Если же ссылка имеет право быть null -ом, то в этом случае нужно корректно обработать и этот случай.

Важное замечание: Если вашу функцию вызывает «внешний мир», вы не должны рассчитывать, что вашей функции передадут хорошие, правильные аргументы. Даже если вы требуете, чтобы объект, который вам передан, не был null -ом, всё равно вам могут передать неправильный объект. Поэтому для функций, доступных внешним модулям, необходимо проверять аргументы на null сразу же в начале кода, и бросать нужное исключение:

Где именно проводить границу между «внутренним» и «внешним» миром, вопрос достаточно нетривиальный. Обычно эта граница есть граница модуля (сборки), или даже той её логической части, которая находится в вашей ответственности. Слишком мелкое дробление ведёт к повторению бессмысленного кода (одна часть программы не доверяет другой и постоянно перепроверяет её). Слишком крупное дробление ведёт к необходимости держать в голове миллионы зависимостей («могу я тут передавать null или нет?»). Пользуйтесь здравым смыслом и личным опытом.

В C# 8 введено, наконец, явное различие между этими двумя случаями. Для тех ссылок, которые могут содержать null , введён специальный синтаксис (в файле должно быть включено #nullable enable (или в проекте enable ):

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

С# 8 без NullReferenceException

.NET спецификация говорит о том, что приложение никогда не должно генерировать NullReferenceException. Впрочем, риск встречи подобного все равно остается во многих библиотеках и приложениях. Де-факто, NullReferenceException – это наиболее часто встречаемый тип исключений. И здесь на сцену выходит C# 8. В новой версии сего прекрасного языка ссылочные типы больше не могут принимать null по-умолчанию. Это и огромный плюс, и отличное нововведение. Но… Это все, конечно, хорошо, но как будут обстоять дела с поддержкой старых библиотек?

Именно в этой статье мы как раз и разберем этот вопрос.

Зачем нам вообще избегать NullReferenceException?

Когда выбрасывается NullReferenceException, причину ошибки далеко не всегда так уж просто найти. Ошибки обычно возникают далеко от очага реальной проблемы. Именно поэтому возникновение подобных ошибок и является крайне нежелательным. Потому вместо проверки на null-исключения просто выбрасывайте ArgumentNullException. Если где-то мы передаем null в качестве аргумента, мы можем просто на уровне компиляции запретить это делать. Просто выбрасываем ArgumentNullException – и мы сразу увидим первопричину ошибки в системе.

Давайте рассмотрим, как именно C# 8 решает подобные проблемы.

Установка C# 8

На момент написания статьи официального релиза C# 8 еще не было. Впрочем, даже сейчас вы можете его опробовать. Сейчас, на момент написания статьи, для этого нужно иметь Visual Studio 2017 15.5-15.7.

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

Читать еще:  Argument not optional vba ошибка

Ссылочные типы больше не могут принимать null

Ничего сложного для понимания здесь нет. Синтаксис, подобный обычным значимым типам. Хотите, чтобы ссылочный тип принимал null? Ставим после оглашения типа знак вопроса.

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

  • При работе со значимыми типами компилятор использует специальный тип Nullable. Это значимый тип, который помимо прочего также содержит в себе приватное булевское поле, определяющее, является ли значение переменной null.
  • Со ссылочными типами компилятор просто добавляет атрибут Nullable. Версия 8 распознает этот атрибут и обрабатывает его соответствующим образом. Версия 7 его не понимает и просто игнорирует.

При компиляции программы под C# 7 Book b и Book? b будут распознаны одинаково.

Приведенный выше тип Book определяет не-nullable свойства Title и Publisher, а также nullable Isbn. Плюс, этот тип также содержит конструктор-кортеж. Используя тип Book и получая значение переменной Isbn, мы можем хранить полученные данные только в переменной типа string?.

Присваивание Nullable к не-Nullable

В случае, если нам нужно присвоить nullable-тип, C# 8 анализирует код. В коде ниже, так как isbn сравнивается с null, после условной конструкции isbn больше не сможет вернуть null. И так как сигнатура метода не предусматривает возвращение string?, при возвращении значения типа произойдет конверсия.

Конечно, эту же конструкцию можно написать гораздо проще и элегантнее.

Возвращение и передача значения

Здесь мы можем видеть класс NewAndGlory, построенный с использованием возможностей последней версии С#. Сигнатура метода GetANullString предусматривает возвращение null, так что в нашем случае этот метод просто возвращает null. Метод GetAString не сможет в свою очередь вернуть null. Что же касательно последнего метода PassAString, тут тоже все очень просто. Мы передаем string и возвращаем также string. По этой причине смысла в проверке на null нет.

С другой стороны, предположим, что у нас есть библиотека TheOldLib, использующая 7 компилятор (задается в файле *.csproj). Класс Legacy определяет метод GetANullString, что просто возвращает null. Метод PassAString принимает строку и проверяет ее на null. Также библиотека определяет интерфейс ILegacyInterface, задающий сигнатуру метода, что возвращает строку. С использованием шарпа 7 версии, мы не можем здесь указать, должна ли строка принимать null, или нет.

Приложение на C# 8 могут использовать библиотеки, созданные и при помощи C# 7

Теперь давайте рассмотрим пример консольного приложения, что ссылается на старые и на новые библиотеки. Используя класс NewAndGlory, в качестве ожидаемого результата метода GetNullString мы можем получить только string?. Попытка же передать null в метод PassAString породит ошибку уровня компиляции (невозможно преобразовать null в не-nullable значение).

Обращаясь к классу Legacy, где метод GetANullString, результат может быть записан в тип string. И, так как эта библиотека не создавалась под эгидой C# 8, наш компилятор будет покорно молчать. Претензии он будет предъявлять только в отношении «новых» сборок. Также здесь мы можем вызвать метод PassAString и спокойно передать в нее null. Если бы компилятор ругался на все подобные нюансы более ранних сборок, список возможных ошибок мог формироваться до бесконечности, поэтому здесь и применяется принцип «разностного отношения».

Метод Foo интерфейса ILegacyInterface, определенный в библиотеке, собранной с использованием более ранней версии языка, – и здесь он возвращает string. Но как же нам тогда его использовать в C# 8? Как можно заметь ниже, здесь интерфейс может быть реализован с использованием как string, так и string?.

Интерфейсы, реализуемые в рамках C# 8, требуют прямого указания поведения по отношению null.

Приложения под C# 7 с использованием сборок C# 8

Что же касательно использования более новых версий сборок в ранних версиях языка, тут нет никаких проблем: все происходит, как и с любыми другими .NET-сборками. Приложение не будет видеть никаких string? – все nullable-ссылочные типы будут интерпретироваться как обычные ссылочные типы — в нашем случае просто как string. И, конечно же, проблема NullReferenceException остается.

Передача в метод PassAString null вызовет NullReferenceException. Для отлавливания подобного в рамках C# ранних версий мы можем проводить ручную проверку на null и выбрасывать ArgumentNullException. Возможно, эта ситуация по отношению к более старым версиям языка в миксе с новыми сборками с дальнейшим развитием C# 8 изменится, но это уже другой вопрос.

В заключение

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

Читать еще:  Краткий доклад на тему операционная система linux

Автор перевода: Евгений Лукашук

Ошибка NullReferenceException — C#

Изучаю C#, хотел уточнить такой вопрос.
Взял скрипт, который запускаю на Unity:

Пытаюсь сделать вывод кол-во нажатий в GUIText:

В результате — пишет ошибку NullReferenceException: Object reference not set to an instance of an object
TextCollvo.Update () (at Assets/TextCollvo.cs:17)

Ну и не считает нажатия — просто выводит «0», как решит подобное?

  • decreat
  • Постоялец

MasterHrust
> public TouchScreen touch = null;

Не в этом ли основная проблема? Нулл референс же.

  • MasterHrust
  • Постоялец

Когда убираю null — та же ошибка именно на строку

  • decreat
  • Постоялец

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

public TouchScreen touch = new TouchScreen();
guiText.text = «Fingers:» + touch.fingerCount;

Но это Unity, может там какая-то своя специфика.

  • MasterHrust
  • Постоялец

decreat
Да, другая — это не прокатывает. Не пойму на что он ругается «Ссылка на объект не указывает на экземпляр объекта»

  • $tatic
  • Постоялец

А guiText создан?

  • MasterHrust
  • Постоялец
  • decreat
  • Постоялец

MasterHrust
You are trying to create a MonoBehaviour using the ‘new’ keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all

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

Воссоздал ситуацию в Unity, дебаггер четко показывает, что touch = null, это причина всех бед. New не работает, это точно специфика Unity.
В гугле есть решения, но мне кажется, что что-то изначально не так сдизайнено. Подтянутся профи в Юнити, мб с ходу подскажут как быть.

  • seaman
  • Постоялец

Вам бы надо для начала пройти несколько уроков по программированию под Юнити. Ну и возможно по программированию вообще.
1. Естественно, как и везде, прежде чем обратиться к члену класса, нужно создать экземпляр класса ( если член не статический). Это Вам уже посоветовали.
2. Однако в Юнити экземпляры классов унаследованных от MonoBehaviour НЕЛЬЗЯ создавать используя new. Их нужно создавать сразу «вешая» на объект. Класс унаследованный от MonoBehaviour может быть только на объекте. Делается это добавлением компонента AddComponent. Да!- любой класс унаследованный от MonoBehaviour — это компонент, и наоборот, любой компонент — это считай некий класс (встроенные часто реализованы на плюсах).
3. Совсем не обязательно создавать рантайм экземпляры классов, унаследованных от MonoBehaviour. Можно заранее их вешать на объекты в инспекторе. Тогда прежде чем к нему обратиться — его нужно найти. Если он на том же объекте, что и скрипт, откуда обращаешься- просто GetComponent. Если на другом объекте — сначала ищем объект (всякого рода Find)/

Ошибка system nullreferenceexception

Модератор форума: beril
Форум игроделов » Движки для разработки игр и сложные системы разработки » Unity » Ошибка NullReferenceException

Ошибка NullReferenceException

ArtemSДата: Понедельник, 20 Марта 2017, 17:51 | Сообщение # 1

В консоли движка выводится ошибка
«NullReferenceException: Object reference not set to an instance of an object PlayerScript.OnDestroy () (at Assets/Scripts/PlayerScript.cs:112)»

в скрипте прописано

void OnDestroy()
<
// Game Over.
var gameOver = FindObjectOfType ();
gameOver.ShowButtons();
>

ошибок никаких нет. Единственное в скрипте подчеркнуто

сказано что устарел метод, надо юзать другой, но у меня почему-то он не работает. Предпологаю, что у меня 5,5,1f, а новый метод начал работать лишь в 5,5,3

Так вот и вопрос, неужели первая ошибка из-за этого или есть какая-то ошибка в синтаксисе. все работает отлично, просто хочу разобраться что за хрень
хуяк, хуяк и в продакшн

Nordicus666Дата: Понедельник, 20 Марта 2017, 18:13 | Сообщение # 2
Reference — ссылка, у тебя в методе OnDestroy, когда ты пытаешься взаимодействовать с gameover (gameover.showbuttons) выходит так, что обращаешься к переменной с null внутри (исходя из текста ошибки), следовательно, когда ты создаешь переменную и запихиваешь в нее FindObjectOfType (), то этот findobject ничего не возвращает, следовательно он ничего не находит
вывод: FindObjectOfType () не находит GameOverScript, ничего не возвращает, в переменной null, ты пытаешься с ней взаимодействовать, вылетает ошибка.
ArtemSДата: Понедельник, 20 Марта 2017, 18:31 | Сообщение # 3

Так получается у меня кнопки удаляются, а потом при смерти появляются.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

///
/// Start or quit the game
///
public class GameOverScript : MonoBehaviour
<
private Button[] buttons;

void Awake()
<
// Get the buttons
buttons = GetComponentsInChildren ();

// Disable them
HideButtons();
>

Ссылка на основную публикацию
Adblock
detector