Tipo di valore vs Tipo di riferimento
In molti linguaggi di programmazione, le variabili hanno quello che viene chiamato un "tipo di dati". I due tipi di dati principali sono tipi di valore (int, float, bool, char, struct, ...) e tipo di riferimento (istanza di classi). Mentre i tipi di valore contengono il valore stesso , i riferimenti contengono un indirizzo di memoria che punta a una porzione di memoria allocata per contenere un set di valori (simile a C / C ++).
Ad esempio, Vector3
è un tipo di valore (una struttura contenente le coordinate e alcune funzioni) mentre i componenti collegati a GameObject (inclusi gli script personalizzati che ereditano da MonoBehaviour
) sono di tipo di riferimento.
Quando posso avere una NullReferenceException?
NullReferenceException
vengono generati quando si tenta di accedere a una variabile di riferimento che non fa riferimento a nessun oggetto, quindi è nulla (l'indirizzo di memoria punta a 0).
Alcuni luoghi comuni a NullReferenceException
saranno sollevati:
Manipolazione di un GameObject / Component che non è stato specificato nella finestra di ispezione
// 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();
}
Recupero di un componente che non è collegato a GameObject e quindi, cercando di manipolarlo:
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 ;
}
Accesso a un GameObject che non esiste:
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";
}
Nota: Fate attenzione, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
restituire solo gameObjects che sono abilitati nella gerarchia quando viene chiamata la funzione.
Cercare di utilizzare il risultato di un getter che sta tornando 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.
Accesso a un elemento di un array non inizializzato
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Meno comune, ma fastidioso se non lo sai sui delegati 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) ;
}
Come risolvere ?
Se hai compreso i paragrafi precedenti, sai come correggere l'errore: assicurati che la tua variabile faccia riferimento (indicando) un'istanza di una classe (o che contenga almeno una funzione per i delegati).
Più facile a dirsi che a farsi? Si Certamente. Ecco alcuni suggerimenti per evitare e identificare il problema.
Il modo "sporco": il metodo try & catch:
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
Il modo "più pulito" (IMHO): il controllo
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) ;
}
Di fronte a un errore che non puoi risolvere, è sempre una buona idea trovare la causa del problema. Se sei "pigro" (o se il problema può essere risolto facilmente), usa Debug.Log
per mostrare sulla console le informazioni che ti aiuteranno a identificare ciò che potrebbe causare il problema. Un modo più complesso è usare i Breakpoint e il Debugger del tuo IDE.
L'uso Debug.Log
è abbastanza utile per determinare quale funzione viene chiamata per prima, ad esempio. Soprattutto se si dispone di una funzione responsabile dell'inizializzazione dei campi. Ma non dimenticare di rimuoverli Debug.Log
per evitare di ingombrare la tua console (e per motivi di prestazioni).
Un altro consiglio, non esitate a "tagliare" le chiamate di funzione e aggiungere Debug.Log
per effettuare alcuni controlli.
Invece di :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Fare questo per verificare se tutti i riferimenti sono impostati:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Anche meglio :
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 ) ;
}
fonti:
- 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