Тип значения против Тип ссылки
Во многих языках программирования переменные имеют так называемый «тип данных». Двумя основными типами данных являются типы значений (int, float, bool, char, struct, ...) и ссылочный тип (экземпляр классов). В то время как типы значений содержат само значение , ссылки содержат адрес памяти, указывающий на часть памяти, выделенную для хранения набора значений (аналогично C / C ++).
Например, Vector3
это тип значения (структура, содержащая координаты и некоторые функции), в то время как компоненты, прикрепленные к вашему GameObject (включая ваши собственные сценарии, наследуемые от MonoBehaviour
), являются ссылочным типом.
Когда я могу получить исключение NullReferenceException?
NullReferenceException
сбрасываются, когда вы пытаетесь получить доступ к ссылочной переменной, которая не ссылается на какой-либо объект, поэтому она имеет значение null (адрес памяти указывает на 0).
Некоторые общие места NullReferenceException
будут подняты:
Управление GameObject / Component, который не был указан в инспекторе
// 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();
}
Извлечение компонента, который не присоединен к GameObject, а затем попытка манипулировать им:
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 ;
}
Доступ к GameObject, который не существует:
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";
}
Примечание: Будьте осторожны, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
возвращать только геймобжекты, которые включены в иерархии , когда функция вызывается.
Попытка использовать результат получателя, который возвращает 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.
Доступ к элементу неинициализированного массива
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Менее распространенный, но раздражающий, если вы не знаете об делегатах C #:
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) ;
}
Как исправить ?
Если вы поняли предыдущие параграфы, вы знаете, как исправить ошибку: убедитесь, что ваша переменная ссылается (указывает на) на экземпляр класса (или содержит хотя бы одну функцию для делегатов).
Проще сказать, чем сделать? Да, в самом деле. Вот несколько советов, чтобы избежать и определить проблему.
«Грязный» способ: метод try & catch:
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
«Чистый» способ (ИМХО): чек
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) ;
}
Когда вы сталкиваетесь с ошибкой, которую вы не можете решить, всегда полезно найти причину проблемы. Если вы «ленивы» (или если проблему легко решить), используйте Debug.Log
для отображения на консоли информацию, которая поможет вам определить причину проблемы. Более сложным способом является использование точек останова и отладчика вашей IDE.
Использование Debug.Log
весьма полезно для определения, например, какая функция вызывается первой. Особенно, если у вас есть функция, отвечающая за инициализацию полей. Но не забудьте удалить их, Debug.Log
чтобы не загромождать вашу консоль (и по соображениям производительности).
Еще один совет, не стесняйтесь «обрезать» свои вызовы функций и добавить, Debug.Log
чтобы сделать некоторые проверки.
Вместо :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Сделайте это, чтобы проверить, установлены ли все ссылки:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Даже лучше :
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 ) ;
}
Источники:
- http://answers.unity3d.com/questions/47830/what-is-a-null-reference-exception-in-unity.html
- /programming/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