Type de valeur vs type de référence
Dans de nombreux langages de programmation, les variables ont ce qu'on appelle un "type de données". Les deux principaux types de données sont les types de valeur (int, float, bool, char, struct, ...) et le type de référence (instance de classes). Alors que les types de valeurs contiennent la valeur elle - même , les références contiennent une adresse mémoire pointant vers une partie de la mémoire allouée pour contenir un ensemble de valeurs (similaire à C / C ++).
Par exemple, Vector3
est un type de valeur (une structure contenant les coordonnées et certaines fonctions) tandis que les composants attachés à votre GameObject (y compris vos scripts personnalisés héritant de MonoBehaviour
) sont de type référence.
Quand puis-je avoir une NullReferenceException?
NullReferenceException
sont levées lorsque vous essayez d'accéder à une variable de référence qui ne fait référence à aucun objet, elle est donc nulle (l'adresse mémoire pointe vers 0).
Quelques lieux communs a NullReferenceException
seront soulevés:
Manipulation d'un GameObject / Component qui n'a pas été spécifié dans l'inspecteur
// 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();
}
Récupérer un composant qui n'est pas attaché au GameObject puis essayer de le manipuler:
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 ;
}
Accéder à un GameObject qui n'existe pas:
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: Attention, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
retour seulement GameObjects qui sont activés dans la hiérarchie lorsque la fonction est appelée.
Essayer d'utiliser le résultat d'un getter qui revient 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.
Accéder à un élément d'un tableau non initialisé
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Moins commun, mais ennuyeux si vous ne le savez pas sur les délégués 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) ;
}
Comment réparer ?
Si vous avez compris les paragraphes précédents, vous savez comment corriger l'erreur: assurez-vous que votre variable référence (pointe vers) une instance d'une classe (ou contient au moins une fonction pour les délégués).
Plus facile à dire qu'à faire? Oui en effet. Voici quelques conseils pour éviter et identifier le problème.
La manière "sale": La méthode try & catch:
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
La voie "plus propre" (IMHO): Le chèque
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) ;
}
Lorsque vous faites face à une erreur que vous ne pouvez pas résoudre, c'est toujours une bonne idée de trouver la cause du problème. Si vous êtes "paresseux" (ou si le problème peut être résolu facilement), utilisez Debug.Log
pour afficher sur la console des informations qui vous aideront à identifier la cause du problème. Une manière plus complexe consiste à utiliser les points d'arrêt et le débogueur de votre IDE.
L'utilisation Debug.Log
est très utile pour déterminer quelle fonction est appelée en premier par exemple. Surtout si vous avez une fonction chargée d'initialiser les champs. Mais n'oubliez pas de les supprimer Debug.Log
pour éviter d'encombrer votre console (et pour des raisons de performances).
Un autre conseil, n'hésitez pas à "couper" vos appels de fonction et à les ajouter Debug.Log
pour faire quelques vérifications.
Au lieu de :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Procédez ainsi pour vérifier si toutes les références sont définies:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Encore mieux :
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
- /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