Тип значення порівняно з типом посилання
У багатьох мовах програмування змінні мають те, що називається "тип даних". Два основні типи даних - це типи значень (int, float, bool, char, structure, ...) та тип посилань (екземпляр класів). У той час як типи значень містять саме значення , посилання містять адресу пам'яті, що вказує на частину пам'яті, виділену, щоб містити набір значень (аналогічно C / C ++).
Наприклад, Vector3
це тип значення (структура, що містить координати та деякі функції), а компоненти, приєднані до вашого GameObject (включаючи власні скрипти, що успадковуються від MonoBehaviour
), є типовим типом.
Коли я можу створити NullReferenceException?
NullReferenceException
викидаються, коли ви намагаєтесь отримати доступ до посилальної змінної, яка не посилається ні на який об'єкт, отже, вона є нульовою (адреса пам'яті вказує на 0).
NullReferenceException
Буде піднято кілька загальних місць а :
Маніпулювання GameObject / компонент, який не вказаний у інспектора
// 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) ;
}
Як виправити?
Якщо ви зрозуміли попередні параграфи, ви знаєте, як виправити помилку: переконайтеся, що ваша змінна посилається (вказує на) екземпляр класу (або містить принаймні одну функцію для делегатів).
Простіше сказати, ніж зробити? Так, справді. Ось кілька порад, щоб уникнути та визначити проблему.
"Брудний" спосіб: метод спробу і лову:
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