Typ wartości a typ odniesienia
W wielu językach programowania zmienne mają tak zwany „typ danych”. Dwa podstawowe typy danych to typy wartości (int, float, bool, char, struct, ...) i typ referencyjny (instancja klas). Podczas gdy typy wartości zawierają samą wartość , odwołania zawierają adres pamięci wskazujący na część pamięci przydzieloną na zbiór wartości (podobny do C / C ++).
Na przykład Vector3
jest typem wartości (strukturą zawierającą współrzędne i niektóre funkcje), podczas gdy komponenty dołączone do GameObject (w tym dziedziczące skrypty niestandardowe MonoBehaviour
) są typem referencyjnym.
Kiedy mogę otrzymać wyjątek NullReferenceException?
NullReferenceException
są rzucane, gdy próbujesz uzyskać dostęp do zmiennej referencyjnej, która nie odwołuje się do żadnego obiektu, dlatego jest pusta (adres pamięci wskazuje 0).
Niektóre typowe miejsca NullReferenceException
zostaną podniesione:
Manipulowanie GameObject / Component, który nie został określony w inspektorze
// 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();
}
Pobieranie komponentu, który nie jest dołączony do GameObject, a następnie próba manipulowania nim:
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 ;
}
Dostęp do GameObject, który nie istnieje:
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";
}
Uwaga: Należy zachować ostrożność, GameObject.Find
, GameObject.FindWithTag
, GameObject.FindObjectOfType
tylko wrócić gameObjects które są włączone w hierarchii, gdy funkcja jest wywoływana.
Próba użycia wyniku zwracającego getter 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.
Dostęp do elementu niezainicjowanej tablicy
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
Mniej powszechne, ale irytujące, jeśli nie wiesz o delegatach 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) ;
}
Jak naprawić ?
Jeśli zrozumiałeś poprzednie paragrafy, wiesz, jak naprawić błąd: upewnij się, że twoja zmienna odwołuje się (wskazuje) na instancję klasy (lub zawiera co najmniej jedną funkcję dla delegatów).
Łatwiej powiedzieć niż zrobić? W rzeczy samej. Oto kilka wskazówek, jak uniknąć i zidentyfikować problem.
„Brudny” sposób: metoda try & catch:
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
„Czystszy” sposób (IMHO): czek
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) ;
}
W obliczu błędu, którego nie można rozwiązać, zawsze dobrze jest znaleźć przyczynę problemu. Jeśli jesteś „leniwy” (lub jeśli problem można łatwo rozwiązać), użyj przycisku, Debug.Log
aby wyświetlić na konsoli informacje, które pomogą ci zidentyfikować przyczynę problemu. Bardziej złożonym sposobem jest użycie punktów przerwania i debuggera IDE.
Użycie Debug.Log
jest na przykład przydatne do określenia, która funkcja jest wywoływana jako pierwsza. Zwłaszcza jeśli masz funkcję odpowiedzialną za inicjowanie pól. Ale nie zapomnij usunąć tych, Debug.Log
aby uniknąć zaśmiecania konsoli (i ze względu na wydajność).
Kolejna rada, nie wahaj się „wyciąć” wywołania funkcji i dodaj, Debug.Log
aby sprawdzić.
Zamiast :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
Zrób to, aby sprawdzić, czy ustawione są wszystkie odwołania:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
Nawet lepiej :
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 ) ;
}
Źródła:
- 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