Program falls into this branch and still there is no crush, which is very unexpected. Object is destroyed and null, however all the data is still the same. For my understanding it should crush with null reference exception. Am I missing some general info about how null works in c# or i ecnountered some known bug in unity/dotnet?
if (begining == null)
{
print(begining.marked);
foreach (var near in begining.nears)
{
print(near);
}
print(begining!.distance);
print(begining.nears_amount);
print(begining.prev);
}
beggining is object of type Node:
public class Node : MonoBehaviour
{
public int nears_amount = 3;
public readonly HashSet<Node> nears = new HashSet<Node>();
public float distance = Single.PositiveInfinity;
public Node? prev = null;
public bool marked = false;
public void connect(Node another);
private void OnDrawGizmos();
public void disconnect_all();
private void onm ouseOver();
public static bool BFS(Node start, Node seeked);
public static List<Node> Dijkstra(Node start, int depth = -1);
public static List<Node> DFS(Node current, int depth);
}
CodePudding user response:
Long story short: In general for things inheriting from UnityEngine.Object
rather use the bool
operator when checking for existence
if (begining)
{
print(begining.marked);
foreach (var near in begining.nears)
{
print(near);
}
print(begining.distance);
print(begining.nears_amount);
print(begining.prev);
}
Note that Unity has a Custom ==
implementation (see also further explanation here) for anything inheriting from UnityEngine.Object
.
This custom ==
is basically be based on
static bool CompareBaseObjects(UnityEngine.Object lhs, UnityEngine.Object rhs) { bool lhsNull = ((object)lhs) == null; bool rhsNull = ((object)rhs) == null; if (rhsNull && lhsNull) return true; if (rhsNull) return !IsNativeObjectAlive(lhs); if (lhsNull) return !IsNativeObjectAlive(rhs); return lhs.m_InstanceID == rhs.m_InstanceID; }
While the underlying System.Object
(= object
) might actually not be null
on the c#
layer, this custom ==
additionally checks if the underlying instance on the c
layer exists or was destroyed (IsNativeObjectAlive
).
Note how Unity doesn't show NullReferenceException
but rather a custom MissingReferenceException
indicating that this System.Object
is actually not really null
but it is rather the Unity operator ==
that returns true
for null
, even if the System.Object
still exists.
This happens as soon as you destroyed an object and then try to access it in the same frame (or better said before GC collects that instance and makes this System.Object
actually null
).
So in general: Do not use == null
but rather the bool
operator for checking for existence (even though the Unity API often uses == null
themselves unfortunately).
Even further: Also never use ?.
, ??
/ ??=
or !.
operators for UnityEngine.Objetc
since they operate on the underlying System.Object
as well and ignore the custom ==
and Equals
implementation of UnityEngine.Object
!