Tipo de valor versus tipo de referência
Em muitas linguagens de programação, as variáveis têm o que é chamado de "tipo de dados". Os dois tipos de dados primários são os tipos de valor (int, float, bool, char, struct, ...) e o tipo de referência (instância de classes). Enquanto os tipos de valor contêm o próprio valor , as referências contêm um endereço de memória apontando para uma parte da memória alocada para conter um conjunto de valores (semelhante ao C / C ++).
Por exemplo, Vector3
é um tipo de valor (uma estrutura que contém as coordenadas e algumas funções), enquanto os componentes anexados ao seu GameObject (incluindo seus scripts personalizados herdados de MonoBehaviour
) são do tipo de referência.
Quando posso ter uma NullReferenceException?
NullReferenceException
são lançadas quando você tenta acessar uma variável de referência que não faz referência a nenhum objeto, portanto é nula (o endereço da memória está apontando para 0).
Alguns lugares comuns a NullReferenceException
serão levantados:
Manipulando um GameObject / Component que não foi especificado no inspetor
// 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 um componente que não está anexado ao GameObject e, em seguida, tentando manipulá-lo:
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 ;
}
Acessando um GameObject que não 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: Tenha cuidado, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
retornar apenas GameObjects que são habilitados na hierarquia quando a função é chamada.
Tentando usar o resultado de um getter que está retornando 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.
Acessando um elemento de uma matriz não inicializada
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Menos comum, mas irritante se você não souber sobre os 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 consertar ?
Se você entendeu os parágrafos anteriores, sabe como corrigir o erro: verifique se sua variável está referenciando (apontando para) uma instância de uma classe (ou contendo pelo menos uma função para delegados).
Mais fácil falar do que fazer? Sim, de fato. Aqui estão algumas dicas para evitar e identificar o problema.
A maneira "suja": o 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) ;
}
O caminho "mais limpo" (IMHO): A verificação
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) ;
}
Ao enfrentar um erro que você não pode resolver, é sempre uma boa idéia encontrar a causa do problema. Se você é "preguiçoso" (ou se o problema pode ser resolvido facilmente), use Debug.Log
para mostrar no console informações que ajudarão você a identificar o que poderia causar o problema. Uma maneira mais complexa é usar os pontos de interrupção e o depurador do seu IDE.
O uso Debug.Log
é bastante útil para determinar qual função é chamada primeiro, por exemplo. Especialmente se você tiver uma função responsável pela inicialização dos campos. Mas não se esqueça de removê-los Debug.Log
para evitar sobrecarregar seu console (e por razões de desempenho).
Outro conselho, não hesite em "cortar" suas chamadas de função e adicionar Debug.Log
para fazer algumas verificações.
Ao invés de :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Faça isso para verificar se todas as referências estão definidas:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Melhor ainda :
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 ) ;
}
Fontes:
- 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-fixfix-it/218510#218510
- https://support.unity3d.com/hc/en-us/articles/206369473-NullReferenceException
- https://unity3d.com/fr/learn/tutorials/topics/scripting/data-types