Tipo de valor vs tipo de referencia
En muchos lenguajes de programación, las variables tienen lo que se llama un "tipo de datos". Los dos tipos de datos principales son los tipos de valor (int, float, bool, char, struct, ...) y el tipo de referencia (instancia de clases). Mientras que los tipos de valor contienen el valor en sí , las referencias contienen una dirección de memoria que apunta a una parte de la memoria asignada para contener un conjunto de valores (similar a C / C ++).
Por ejemplo, Vector3
es un tipo de valor (una estructura que contiene las coordenadas y algunas funciones) mientras que los componentes adjuntos a su GameObject (incluidos sus scripts personalizados que heredan MonoBehaviour
) son de tipo de referencia.
¿Cuándo puedo tener una NullReferenceException?
NullReferenceException
se lanzan cuando intenta acceder a una variable de referencia que no hace referencia a ningún objeto, por lo tanto, es nula (la dirección de memoria apunta a 0).
Se NullReferenceException
plantearán algunos lugares comunes a :
Manipular un GameObject / Component que no se haya especificado en el 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();
}
Recuperando un componente que no está conectado al GameObject y luego, tratando de manipularlo:
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 ;
}
Acceder a un GameObject que no existe:
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: Tenga cuidado, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
devolver sólo GameObjects que están habilitadas en la jerarquía cuando se invoca la función.
Intentando usar el resultado de un getter que está regresando 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.
Acceder a un elemento de una matriz no inicializada
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Menos común, pero molesto si no lo sabe sobre los delegados de 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) ;
}
Como arreglar ?
Si ha entendido los párrafos anteriores, sabe cómo corregir el error: asegúrese de que su variable haga referencia (señale) a una instancia de una clase (o que contenga al menos una función para delegados).
¿Es más fácil decirlo que hacerlo? Si, de hecho. Aquí hay algunos consejos para evitar e identificar el problema.
La forma "sucia": el método 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 forma "más limpia" (en mi humilde opinión): el cheque
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) ;
}
Cuando enfrenta un error que no puede resolver, siempre es una buena idea encontrar la causa del problema. Si es "flojo" (o si el problema se puede resolver fácilmente), use Debug.Log
para mostrar en la consola información que lo ayudará a identificar qué podría causar el problema. Una forma más compleja es utilizar los puntos de interrupción y el depurador de su IDE.
El uso Debug.Log
es bastante útil para determinar qué función se llama primero, por ejemplo. Especialmente si tiene una función responsable de inicializar campos. Pero no olvide eliminarlos Debug.Log
para evitar abarrotar su consola (y por razones de rendimiento).
Otro consejo, no dude en "cortar" sus llamadas a funciones y agregarlas Debug.Log
para hacer algunas verificaciones.
En lugar de :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Haga esto para verificar si todas las referencias están establecidas:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Aun mejor :
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 ) ;
}
Fuentes:
- 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