So, I’ve worked in c# for a few years and I’ve never seen anything like this and it’s got me stumped. Basically I have a generic method that returns an array. It shouldn’t return any null values. In fact, right before it returns the array, I have it set to test if any values are null and print out a debug if they are. See:
public T[] Get ( int x, int y )
{
T[] test = GetRange(x,y,x,y);
foreach ( T t in test )
{
if ( t == null ) { Debug.Log("yo"); }
}
return test;
}
And then after that I use the result from this function like so:
public Tile[] GetTilesAtPosition ( Vector3 tilePosition )
{
//Cache position variables
int getX = Mathf.RoundToInt(tilePosition.x);
int getY = Mathf.RoundToInt(tilePosition.y);
//Get the Tiles at the desired position
Tile[] test = currentTiles.Get (getX, getY);
foreach ( Tile t in test )
{
if ( t == null ) { Debug.Log("YO"); }
}
return test;
}
Even though I JUST checked all of the values in this array to see if any of them were null, and none of them were, now that I’ve passed it over to this function, testing it again DOES say some are null. So, “yo” is never printed but “YO” is, even though nothing should have changed between the two. Is there any reason something like this could happen?
I see several possible problems here. First of all what kind of classes you have in your array? MonoBehaviours or other Unity objects? Or do you use it for your own classes which aren’t derived from a Unity class?
You should use “class” as generic constraint if you want it to be a nullable type.
public class YourClass<T> where T : class
{
public T[] Get ( int x, int y )
{
//...
}
}
I assumed you have a generic class since a generic method wouldn’t make much sense since you call “GetRange” in there without any generic parameter.
If your Tile class is derived from MonoBehaviour or ScriptableObject, it’s possible that the generic null test returns “not null” but the explicit null test returns “null”.
This is because Unity overloads the == operator and the Equals method to “fake” that an object is null if it’s native counter part has been destroyed or doesn’t exist. They might even remove the overload in the future.
The problem here is that the generic method uses System.Object’s == operator since it doesn’t know the type beforehand. The == operator is not a virtual method which can be overridden.
See this example:
public Collider c; // assigned in the inspector
void Start()
{
DestroyImmediate(c);
// executes the overloaded == operator. This "if" will be executed
if (c == null)
Debug.Log("c is Null");
object o = c; // copy the reference to a System.Object variable
// executes the System.Object's == operator. This "if" will not be executed since the object sill exists
if (o == null)
Debug.Log("o is Null");
}
Unity objects become “fake null” if:
the object got destroyed, either with Destroy / DestroyImmediate or when a new scene is loaded.
you create an object derived from MonoBehaviour or ScriptableObject with it’s constructor manually instead using AddComponent / CreateInstance.