Причина
Вкратце
Вы пытаетесь воспользоваться чем-то, что равно null
(или Nothing
в VB.NET). Это означает, что либо вы присвоили это значение, либо вы ничего не присваивали.
Как и любое другое значение, null
может передаваться от объекта к объекту, от метода к методу. Если нечто равно null
в методе «А», вполне может быть, что метод «В» передал это значение в метод «А».
Остальная часть статьи описывает происходящее в деталях и перечисляет распространённые ошибки, которые могут привести к исключению NullReferenceException
.
Более подробно
Если среда выполнения выбрасывает исключение NullReferenceException
, то это всегда означает одно: вы пытаетесь воспользоваться ссылкой. И эта ссылка не инициализирована (или была инициализирована, но уже не инициализирована).
Это означает, что ссылка равна null
, а вы не сможете вызвать методы через ссылку, равную null
. В простейшем случае:
string foo = null;
foo.ToUpper();
Этот код выбросит исключение NullReferenceException
на второй строке, потому что вы не можете вызвать метод ToUpper()
у ссылки на string
, равной null
.
Отладка
Как определить источник ошибки? Кроме изучения, собственно, исключения, которое будет выброшено именно там, где оно произошло, вы можете воспользоваться общими рекомендациями по отладке в Visual Studio: поставьте точки останова в ключевых точках, изучите значения переменных, либо расположив курсор мыши над переменной, либо открыв панели для отладки: Watch, Locals, Autos.
Если вы хотите определить место, где значение ссылки устанавливается или не устанавливается, нажмите правой кнопкой на её имени и выберите «Find All References». Затем вы можете поставить точки останова на каждой найденной строке и запустить приложение в режиме отладки. Каждый раз, когда отладчик остановится на точке останова, вы можете удостовериться, что значение верное.
Следя за ходом выполнения программы, вы придёте к месту, где значение ссылки не должно быть null
, и определите, почему не присвоено верное значение.
Примеры
Несколько общих примеров, в которых возникает исключение.
Цепочка
ref1.ref2.ref3.member
Если ref1
, ref2
или ref3
равно null
, вы получите NullReferenceException
. Для решения проблемы и определения, что именно равно null
, вы можете переписать выражение более простым способом:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
Например, в цепочке HttpContext.Current.User.Identity.Name
, значение может отсутствовать и у HttpContext.Current
, и у User
, и у Identity
.
Неявно
public class Person {
public int Age { get; set; }
}
public class Book {
public Person Author { get; set; }
}
public class Example {
public void Foo() {
Book b1 = new Book();
int authorAge = b1.Author.Age; // Свойство Author не было инициализировано
// нет Person, у которого можно вычислить Age.
}
}
То же верно для вложенных инициализаторов:
Book b1 = new Book { Author = { Age = 45 } };
Несмотря на использование ключевого слова new
, создаётся только экземпляр класса Book
, но экземпляр Person
не создаётся, поэтому свойство Author
остаётся null
.
Массив
int[] numbers = null;
int n = numbers[0]; // numbers = null. Нет массива, чтобы получить элемент по индексу
Элементы массива
Person[] people = new Person[5];
people[0].Age = 20; // people[0] = null. Массив создаётся, но не
// инициализируется. Нет Person, у которого можно задать Age.
Массив массивов
long[][] array = new long[1][];
array[0][0] = 3; // = null, потому что инициализировано только первое измерение.
// Сначала выполните array[0] = new long[2].
Collection/List/Dictionary
Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames = null.
// Экземпляр словаря не создан.
LINQ
public class Person {
public string Name { get; set; }
}
var people = new List<Person>();
people.Add(null);
var names = from p in people select p.Name;
string firstName = names.First(); // Исключение бросается здесь, хотя создаётся
// строкой выше. p = null, потому что
// первый добавленный элемент = null.
События
public class Demo
{
public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e)
{
StateChanged(this, e); // Здесь бросится исключение, если на
// событие StateChanged никто не подписался
}
}
Неудачное именование переменных
Если бы в коде ниже у локальных переменных и полей были разные имена, вы бы обнаружили, что поле не было инициализировано:
public class Form1 {
private Customer customer;
private void Form1_Load(object sender, EventArgs e) {
Customer customer = new Customer();
customer.Name = "John";
}
private void Button_Click(object sender, EventArgs e) {
MessageBox.Show(customer.Name);
}
}
Можно избежать проблемы, если использовать префикс для полей:
private Customer _customer;
Цикл жизни страницы ASP.NET
public partial class Issues_Edit : System.Web.UI.Page
{
protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Выполняется только на первой загрузке, но не когда нажата кнопка
myIssue = new TestIssue();
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
myIssue.Entry = "NullReferenceException здесь!";
}
}
Сессии ASP.NET
// Если сессионная переменная "FirstName" ещё не была задана,
// то эта строка бросит NullReferenceException.
string firstName = Session["FirstName"].ToString();
Пустые вью-модели ASP.NET MVC
Если вы возвращаете пустую модель (или свойство модели) в контроллере, то вью бросит исключение при попытке доступа к ней:
// Controller
public class Restaurant:Controller
{
public ActionResult Search()
{
return View(); // Модель не задана.
}
}
// Razor view
@foreach (var restaurantSearch in Model.RestaurantSearch) // Исключение.
{
}
Способы избежать
Явно проверять на null
, пропускать код
Если вы ожидаете, что ссылка в некоторых случаях будет равна null
, вы можете явно проверить на это значение перед доступом к членам экземпляра:
void PrintName(Person p) {
if (p != null) {
Console.WriteLine(p.Name);
}
}
Явно проверять на null
, использовать значение по умолчанию
Методы могут возвращать null
, например, если не найден требуемый экземпляр. В этом случае вы можете вернуть значение по умолчанию:
string GetCategory(Book b) {
if (b == null)
return "Unknown";
return b.Category;
}
Явно проверять на null
, выбрасывать своё исключение
Вы также можете бросать своё исключение, чтобы позже его поймать:
string GetCategory(string bookTitle) {
var book = library.FindBook(bookTitle); // Может вернуть null
if (book == null)
throw new BookNotFoundException(bookTitle); // Ваше исключение
return book.Category;
}
Использовать Debug.Assert
для проверки на null
для обнаружения ошибки до бросания исключения
Если во время разработки вы знаете, что метод может, но вообще-то не должен возвращать null
, вы можете воспользоваться Debug.Assert
для быстрого обнаружения ошибки:
string GetTitle(int knownBookID) {
// Вы знаете, что метод не должен возвращать null
var book = library.GetBook(knownBookID);
// Исключение будет выброшено сейчас, а не в конце метода.
Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Остальной код...
return book.Title; // Не выбросит NullReferenceException в режиме отладки.
}
Однако эта проверка не будет работать в релизной сборке, и вы снова получите NullReferenceException
, если book == null
.
Использовать GetValueOrDefault()
для Nullable типов
DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Отобразит значение по умолчанию, потому что appointment = null.
appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Отобразит дату, а не значение по умолчанию.
Использовать оператор ??
(C#) или If()
(VB)
Краткая запись для задания значения по умолчанию:
IService CreateService(ILogger log, Int32? frobPowerLevel)
{
var serviceImpl = new MyService(log ?? NullLog.Instance);
serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}
Использовать операторы ?.
и ?[
(C# 6+, VB.NET 14+):
Это оператор безопасного доступа к членам, также известный как оператор Элвиса за специфическую форму. Если выражение слева от оператора равно null
, то правая часть игнорируется, и результатом считается null
. Например:
var title = person.Title.ToUpper();
Если свойство Title
равно null
, то будет брошено исключение, потому что это попытка вызвать метод ToUpper
на значении, равном null
. В C# 5 и ниже можно добавить проверку:
var title = person.Title == null ? null : person.Title.ToUpper();
Теперь вместо бросания исключения переменной title
будет присвоено null
. В C# 6 был добавлен более короткий синтаксис:
var title = person.Title?.ToUpper();
Разумеется, если переменная person
может быть равна null
, то надо проверять и её. Также можно использовать операторы ?.
и ??
вместе, чтобы предоставить значение по умолчанию:
// обычная проверка на null
int titleLength = 0;
if (title != null)
titleLength = title.Length;
// совмещаем операторы `?.` и `??`
int titleLength = title?.Length ?? 0;
Если любой член в цепочке может быть null
, то можно полностью обезопасить себя (хотя, конечно, архитектуру стоит поставить под сомнение):
int firstCustomerOrderCount = customers?[0]?.Orders?.Count() ?? 0;
NullReferenceException
возникает, когда вы пытаетесь получить доступ к ссылочной переменной, которая не ссылается на какой-либо объект. Если ссылочная переменная не ссылается на объект, она будет рассматриваться как null
. Время выполнения сообщит вам, что вы пытаетесь получить доступ к объекту, когда переменная имеет значение null
, создав исключение NullReferenceException
.
Ссылочные переменные в C# и JavaScript по своей концепции аналогичны указателям в C и C++. Типы ссылок по умолчанию имеют значение null
, чтобы указать, что они не ссылаются на какой-либо объект. Следовательно, если вы попытаетесь получить доступ к объекту, на который ссылаются, а его нет, вы получите NullReferenceException
.
Когда вы получаете NullReferenceException
в своем коде, это означает, что вы забыли установить переменную перед ее использованием. Сообщение об ошибке будет выглядеть примерно так:
NullReferenceException: Object reference not set to an instance of an object
at Example.Start () [0x0000b] in /Unity/projects/nre/Assets/Example.cs:10
В этом сообщении об ошибке говорится, что NullReferenceException
произошло в строке 10 файла сценария Example.cs
. Кроме того, в сообщении говорится, что исключение произошло внутри функции Start()
. Это упрощает поиск и исправление исключения нулевой ссылки. В этом примере код такой:
//c# example
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
// Use this for initialization
void Start () {
__GameObject__The fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject's functionality is defined by the Components attached to it. [More info](class-GameObject)See in [Glossary](Glossary#GameObject) go = GameObject.Find("wibble");
Debug.Log(go.name);
}
}
Код просто ищет игровой объект под названием «wibble». В этом примере нет игрового объекта с таким именем, поэтому функция Find()
возвращает null
. В следующей строке (строка 9) мы используем переменную go
и пытаемся вывести имя игрового объекта, на который она ссылается. Поскольку мы обращаемся к несуществующему игровому объекту, среда выполнения выдает нам NullReferenceException
Нулевые проверки
Хотя это может расстраивать, когда это происходит, это просто означает, что сценарий должен быть более осторожным. Решение в этом простом примере состоит в том, чтобы изменить код следующим образом:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
void Start () {
GameObject go = GameObject.Find("wibble");
if (go) {
Debug.Log(go.name);
} else {
Debug.Log("No game object called wibble found");
}
}
}
Теперь, прежде чем пытаться что-то делать с переменной go
, мы проверяем, не является ли она null
. Если это null
, мы показываем сообщение.
Попробовать/перехватить блоки
Другой причиной NullReferenceException
является использование переменной, которая должна быть инициализирована в ИнспектореОкно Unity, в котором отображается информация о текущем выбранном игровом объекте, активе или настройках проекта, что позволяет просматривать и редактировать значения. Дополнительная информация
См. в Словарь. Если вы забудете это сделать, переменная будет иметь значение null
. Другой способ справиться с NullReferenceException
— использовать блок try/catch. Например, этот код:
using UnityEngine;
using System;
using System.Collections;
public class Example2 : MonoBehaviour {
public Light myLight; // set in the inspector
void Start () {
try {
myLight.color = Color.yellow;
}
catch (NullReferenceException ex) {
Debug.Log("myLight was not set in the inspector");
}
}
}
В этом примере кода переменная с именем myLight
— это Light
, которую следует установить в окне инспектора. Если эта переменная не задана, по умолчанию она будет иметь значение null
. Попытка изменить цвет света в блоке try
вызывает NullReferenceException
, которое подхватывается блоком catch
. Блок catch
отображает сообщение, которое может быть более полезным для художников и геймдизайнеров, и напоминает им о необходимости установить свет в инспекторе.
Обзор
-
NullReferenceException
возникает, когда ваш код скрипта пытается использовать переменную, которая не установлена (ссылка) и объект. - Отображаемое сообщение об ошибке многое говорит о том, в каком месте кода возникает проблема.
NullReferenceException
можно избежать, написав код, который проверяетnull
перед доступом к объекту или использует блоки try/catch.
A NullReferenceException
happens when you try to access a reference variable that isn’t referencing any object. If a reference variable isn’t referencing an object, then it’ll be treated as null
. The run-time will tell you that you are trying to access an object, when the variable is null
by issuing a NullReferenceException
.
Reference variables in c# and JavaScript are similar in concept to pointers in C and C++. Reference types default to null
to indicate that they are not referencing any object. Hence, if you try and access the object that is being referenced and there isn’t one, you will get a NullReferenceException
.
When you get a NullReferenceException
in your code it means that you have forgotten to set a variable before using it. The error message will look something like:
NullReferenceException: Object reference not set to an instance of an object
at Example.Start () [0x0000b] in /Unity/projects/nre/Assets/Example.cs:10
This error message says that a NullReferenceException
happened on line 10 of the script file Example.cs
. Also, the message says that the exception happened inside the Start()
function. This makes the Null Reference Exception easy to find and fix. In this example, the code is:
//c# example
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
// Use this for initialization
void Start () {
__GameObject__The fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject's functionality is defined by the Components attached to it. [More info](class-GameObject.html)<span class="tooltipGlossaryLink">See in [Glossary](Glossary.html#GameObject)</span> go = GameObject.Find("wibble");
Debug.Log(go.name);
}
}
The code simply looks for a game object called “wibble”. In this example there is no game object with that name, so the Find()
function returns null
. On the next line (line 9) we use the go
variable and try and print out the name of the game object it references. Because we are accessing a game object that doesn’t exist the run-time gives us a NullReferenceException
Null Checks
Although it can be frustrating when this happens it just means the script needs to be more careful. The solution in this simple example is to change the code like this:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
void Start () {
GameObject go = GameObject.Find("wibble");
if (go) {
Debug.Log(go.name);
} else {
Debug.Log("No game object called wibble found");
}
}
}
Now, before we try and do anything with the go
variable, we check to see that it is not null
. If it is null
, then we display a message.
Try/Catch Blocks
Another cause for NullReferenceException
is to use a variable that should be initialised in the InspectorA Unity window that displays information about the currently selected GameObject, asset or project settings, allowing you to inspect and edit the values. More info
See in Glossary. If you forget to do this, then the variable will be null
. A different way to deal with NullReferenceException
is to use try/catch block. For example, this code:
using UnityEngine;
using System;
using System.Collections;
public class Example2 : MonoBehaviour {
public Light myLight; // set in the inspector
void Start () {
try {
myLight.color = Color.yellow;
}
catch (NullReferenceException ex) {
Debug.Log("myLight was not set in the inspector");
}
}
}
In this code example, the variable called myLight
is a Light
which should be set in the Inspector window. If this variable is not set, then it will default to null
. Attempting to change the color of the light in the try
block causes a NullReferenceException
which is picked up by the catch
block. The catch
block displays a message which might be more helpful to artists and game designers, and reminds them to set the light in the inspector.
Summary
-
NullReferenceException
happens when your script code tries to use a variable which isn’t set (referencing) and object. - The error message that appears tells you a great deal about where in the code the problem happens.
-
NullReferenceException
can be avoided by writing code that checks fornull
before accessing an object, or uses try/catch blocks.
Debugging System.NullReferenceException — Object reference not set to an instance of an object
TOC
Time for another post in the series Debugging common .NET exceptions. Today’s exception is, without a doubt, the error most people have experienced: System.NullReferenceException. The exception happens when you try to invoke a reference that you were expecting to point to an object but in fact, points to null
. Let’s get started!
Handling the error
There are some clever ways to avoid a NullReferenceException
, but before we start looking into those, let us see how the exception can be caught. Being a plain old C# exception, NullReferenceException
can be caught using a try/catch
:
try
{
string s = null;
s.ToString();
}
catch (NullReferenceException e)
{
// Do something with e, please.
}
Running the code above will produce the following error:
System.NullReferenceException: Object reference not set to an instance of an object.
Debugging the error
We already know why the exception is happening. Something is null
. When looking at the code above, it’s clear that s
is null and the stack trace even tells us that:
Sometimes spotting what is null
can be hard. Take a look at the following example:
var street = service.GetUser().Address.Street;
If the code above throws a NullReferenceException
, what is null
? service
? The result of GetUser()
? Address
? At first glance, Visual Studio isn’t exactly helpful either:
There is a range of different ways to find out what is going on. Let’s look at the most commonly used ones.
Splitting chained method-calls to multiple lines
Spotting which call that caused an error is a lot easier if the calls are split into multiple lines:
var service = new Service();
var user = service.GetUser();
var address = user.Address;
var street = address.Street;
Running the code reveals the actual call causing the exception:
In the example above user.Address
returns null, why address.Street
causes the NullReferenceException
.
While splitting code into atoms like this can help debug what is going wrong, it’s not preferable in terms of readability (IMO).
Using Null Reference Analysis in Visual Studio
If you are on Visual Studio 2017 or newer (if not, now is the time to upgrade), you will have the Null Reference Analysis feature available. With this in place, Visual Studio can show you exactly what is null. Let’s change the example back to method-chaining:
var street = service.GetUser().Address.Street;
To enable the analysis go to Debug | Windows | Exception Settings. Check Common Language Runtime Exceptions (if not already checked) or extend the node and check the exceptions you are interested in. In this case, you can check System.NullReferenceException. When running the code, the debugger breaks on the NullReferenceException
and you now see the Exception Thrown window:
Voila! The window says «ConsoleApp18.User.Address.get returned null». Exactly what we wanted to see. This will require you to run the code locally, though. If you are experiencing the exception on your production website, the Null Reference Analysis will not be available, since this is a feature belonging to Visual Studio (unfortunately). With that said, you can attach a debugger to a remote site running on Azure as explained here: Introduction to Remote Debugging on Azure Web Sites.
Fixing the error
There are various ways to fix NullReferenceException
. We’ll start with the simple (but dirty) approach.
Using null checks
If null
is an allowed value of an object, you will need to check for it. The most simple solution is to include a bunch of if
-statements.
if (service != null)
{
var user = service.GetUser();
if (user != null)
{
var address = user.Address;
if (address != null)
{
var street = address.Street;
}
}
}
The previous code will only reach address.Street
if everything else is not null
. We can probably agree that the code isn’t exactly pretty. Having multiple nested steps is harder to read. We can reverse the if
-statements:
if (service == null) return;
var user = service.GetUser();
if (user == null) return;
var address = user.Address;
if (address == null) return;
var street = address.Street;
Simpler, but still a lot of code to get a street name.
Using null-conditional operator
C# 6 introduced a piece of syntactic sugar to check for null
: null-conditional operator. Let’s change the method-chain example from before to use the «new» operator:
var user = service?.GetUser()?.Address?.Street;
The ?
to the right of each variable, corresponds the nested if
-statements from previously. But with much less code.
Use Debug.Assert during development
When getting a NullReferenceException
it can be hard to spot the intent with the code from the original developer. Rather than including if
-statements, it can be clearer for future authors of your code to use the Debug.Assert
-method. Much like in a xUnit or NUnit test, you use Assert
to verify the desired state on your objects. In the example from above, the service
object could have come through a parameter or a constructor injected dependency:
class MyClass
{
Service service;
public MyClass(Service service)
{
this.service = service;
}
public string UserStreet()
{
return service.GetUser().Address.Street;
}
}
To make a statement in your code that service
should never be allowed to have the value of null
, extend the constructor:
public MyClass(Service service)
{
Debug.Assert(service != null);
this.service = service;
}
In the case MyClass
is constructed with null
, the following error is shown when running locally:
Use nullable reference types in C# 8.0
When designing code you often end up expecting parameters to be not null
but end up checking for null
to avoid a NullReferenceException
. As you already know, all reference types in C# can take the value of null
. Value types like int
and boolean
cannot take a value of null
unless explicitely specified using the nullable value type (int?
or Nullable<int>
). Maybe it should have been the other way around with reference types all along?
C# 8 can fix this with nullable reference types (maybe NOT nullable reference types is a better name). Since this is a breaking change, it is launched as an opt-in feature. Nullable reference types are a great way to avoid NullReferenceException
s, since you are very explicit about where you expect null
and where not.
To enable not nullable reference types, create a new .NET Core 3 project and add the following to the csproj
file:
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
You should be on C# 8 already, but to make it explicit, I’ve added the LangVersion
element. The Nullable
element enables nullable reference types. Out of the box, C# 8 creates a warning if it identifies the use of null
where a value is expected. Let’s see how that looks:
class Program
{
static void Main()
{
new Program().SayHello(null);
}
public void SayHello(string msg)
{
Console.WriteLine(msg);
}
}
When compiling we see the following warning:
Program.cs(9,36): warning CS8625: Cannot convert null literal to non-nullable reference type. [C:projectscore3core3.csproj]
I know you are not one of them, but some people developed a warning-resistance which means that warnings are simply ignored. To overcome this, make sure that errors like this causes build errors over warnings by adding the following to csproj
:
<WarningsAsErrors>CS8602,CS8603,CS8618,CS8625</WarningsAsErrors>
This will tell the C# compiler to treat these four nullable reference type warnings as errors instead.
Just to make it clear, allowing null in the msg
parameter, you use the ?
characters as with value types:
public void SayHello(string? msg)
{
...
}
Logging and monitoring
Logging and monitoring for null reference exceptions are a must. While some developers tempt to create control flow from exceptions (no-one should), null reference exceptions should never happen. This means that a System.NullReferenceException
is a type of exception that should always be logged and fixed. A null check through either an if
statement or the null-conditional operator is always the preferred way of handling potential null values. But make sure to implement a logging strategy that logs all uncaught exceptions, including the System.NullReferenceException
.
When logging a System.NullReferenceException
in a log file, database, elmah.io, or similar, it can be hard to spot what is null. You typically only see the method-name that causes the NullReferenceException
and the Null Reference Analysis feature is only available while debugging inside Visual Studio. I will recommend you to always Include filename and line number in stack traces. This will pinpoint the exact line where the error happens.
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes
Monitor your website
The “Object reference not set to an instance of an object” is a very famous error in C# that appears when you get a NullReferenceException. This occurs when you try to access a property or method of an object that points to a null value. They can be fixed using Null conditional operators and handled using try-catch blocks.
In this post, we will learn more about the error and the ways to fix it.
What is “NullReferenceException: Object reference not set to an instance of an object” error?
As mentioned earlier, the NullReferenceException indicates that your code is trying to work with an object that has a null value as its reference. This means that the reference object has not been initialized.
This is a runtime exception that can be caught using a try-catch block.
Example code
try
{
string a = null;
a.ToString();
}
catch (NullReferenceException e)
{
//Code to do something with e
}
How to fix this error?
You can fix this error by using the following methods:
- Using Null conditional operators
- Using the Null Coalescing operator
- Using nullable datatypes in C#
1) Using Null conditional operators
This method is easier than using an if-else condition to check whether the variable value is null. Look at this example,
int? length = customers?.Length; // this will return null if customers is null, instead of throwing the exception
2) Using the Null Coalescing operator
This operator looks like “??” and provides a default value to variables that have a null value. It is compatible with all nullable datatypes.
Example
int length = customers?.Length ?? 0; // 0 is provided by default if customers is null
3) Using nullable datatypes in C#
All reference types in C# can have a null value. But some data types such as int and Boolean cannot take null values unless they are explicitly defined. This is done by using Nullable data types.
For example,
static int Add(string roll_numbers)
{
return roll_numbers.Split(","); // This code might throw a NullReferenceException as roll_numbers variable can be null
}
Correct code
static int Add(string? roll_numbers) // As roll_numbers argument can now be null, the NullReferenceException can be avoided
{
return roll_numbers.Split(",");
}
The best way to avoid the «NullReferenceException: Object reference not set to an instance of an object” error is to check the values of all variables while coding. You can also use a simple if-else statement to check for null values, such as if (numbers!=null) to avoid this exception.
Value type vs Reference type
In many programming languages, variables have what is called a «data type». The two primary data types are value types (int, float, bool, char, struct, …) and reference type (instance of classes). While value types contains the value itself, references contains a memory address pointing to a portion of memory allocated to contain a set of values (similar to C/C++).
For example, Vector3
is a value type (a struct containing the coordinates and some functions) while components attached to your GameObject (including your custom scripts inheriting from MonoBehaviour
) are reference type.
When can I have a NullReferenceException?
NullReferenceException
are thrown when you try to access a reference variable that isn’t referencing any object, hence it is null (memory address is pointing to 0).
Some common places a NullReferenceException
will be raised:
Manipulating a GameObject / Component that has not been specified in the inspector
// t is a reference to a Transform.
public Transform t ;
private void Awake()
{
// If you do not assign something to t
// (either from the Inspector or using GetComponent), t is null!
t.Translate();
}
Retrieving a component that isn’t attached to the GameObject and then, trying to manipulate it:
private void Awake ()
{
// Here, you try to get the Collider component attached to your gameobject
Collider collider = gameObject.GetComponent<Collider>();
// But, if you haven't any collider attached to your gameobject,
// GetComponent won't find it and will return null, and you will get the exception.
collider.enabled = false ;
}
Accessing a GameObject that doesn’t exist:
private void Start()
{
// Here, you try to get a gameobject in your scene
GameObject myGameObject = GameObject.Find("AGameObjectThatDoesntExist");
// If no object with the EXACT name "AGameObjectThatDoesntExist" exist in your scene,
// GameObject.Find will return null, and you will get the exception.
myGameObject.name = "NullReferenceException";
}
Note: Be carefull, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
only return gameObjects that are enabled in the hierarchy when the function is called.
Trying to use the result of a getter that’s returning null
:
var fov = Camera.main.fieldOfView;
// main is null if no enabled cameras in the scene have the "MainCamera" tag.
var selection = EventSystem.current.firstSelectedGameObject;
// current is null if there's no active EventSystem in the scene.
var target = RenderTexture.active.width;
// active is null if the game is currently rendering straight to the window, not to a texture.
Accessing an element of a non-initialized array
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Less common, but annoying if you don’t know it about C# delegates:
delegate double MathAction(double num);
// Regular method that matches signature:
static double Double(double input)
{
return input * 2;
}
private void Awake()
{
MathAction ma ;
// Because you haven't "assigned" any method to the delegate,
// you will have a NullReferenceException
ma(1) ;
ma = Double ;
// Here, the delegate "contains" the Double method and
// won't throw an exception
ma(1) ;
}
How to fix ?
If you have understood the previous paragraphes, you know how to fix the error: make sure your variable is referencing (pointing to) an instance of a class (or containing at least one function for delegates).
Easier said than done? Yes, indeed. Here are some tips to avoid and identify the problem.
The «dirty» way : The try & catch method :
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
The «cleaner» way (IMHO) : The check
Collider collider = gameObject.GetComponent<Collider>();
if(collider != null)
{
// You can safely manipulate the collider here
collider.enabled = false;
}
else
{
Debug.LogError("Oops, there is no collider attached", this) ;
}
When facing an error you can’t solve, it’s always a good idea to find the cause of the problem. If you are «lazy» (or if the problem can be solved easily), use Debug.Log
to show on the console information which will help you identify what could cause the problem. A more complex way is to use the Breakpoints and the Debugger of your IDE.
Using Debug.Log
is quite useful to determine which function is called first for example. Especially if you have a function responsible for initializing fields. But don’t forget to remove those Debug.Log
to avoid cluttering your console (and for performance reasons).
Another advice, don’t hesitate to «cut» your function calls and add Debug.Log
to make some checks.
Instead of :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Do this to check if every references are set :
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Even better :
GameObject myObject = GameObject.Find("MyObject") ;
if( myObject != null )
{
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
if( superComponent != null )
{
superComponent.value = "foo" ;
}
else
{
Debug.Log("No SuperComponent found onMyObject!");
}
}
else
{
Debug.Log("Can't find MyObject!", this ) ;
}
Sources:
- http://answers.unity3d.com/questions/47830/what-is-a-null-reference-exception-in-unity.html
- https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it/218510#218510
- https://support.unity3d.com/hc/en-us/articles/206369473-NullReferenceException
- https://unity3d.com/fr/learn/tutorials/topics/scripting/data-types