Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 11 Next capture
2021 2022 2023
1 capture
11 Jun 22 - 11 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by dustxx · Jul 07, 2015 at 09:55 AM · c#arraynullreferenceexceptiongenericspassing

Why is passing this array causing some of its values to become null?

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?

Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Nerevar · Jul 07, 2015 at 11:12 AM 1
Share

$$anonymous$$aybe we could see the GetRange() function? it might the one returning null values. Try to do a better Debug.Log to get more relevant information on the tiles. Also use for() loop it will be less confusing and check that both array have the same length from one function to another.

2 Replies

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by Bunny83 · Jul 07, 2015 at 12:48 PM

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.

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image dustxx · Jul 07, 2015 at 01:00 PM 0
Share

Wow, thanks for such a detailed answer! You were right about my use of the 'fake null' check. So, I've rewritten the generic class to only contain subclasses of one of my monobehaviors, so this check will work there. $$anonymous$$y code's all working now, yay! I'll keep in $$anonymous$$d that unity overloads the == operator like this, this actually explains a few problems I've had in the past now that I think about it. Next time I write a generic class like this I'll remember this! Thanks so much again!!

avatar image Mouton · Jul 07, 2015 at 01:19 PM 0
Share

Thank you for the clarifications, seems I was a bit confused because I didn't knew about this operator overload. @dustxx : you should mark this solution as the "anwser" since it is better answer than $$anonymous$$e.

avatar image dustxx · Jul 07, 2015 at 01:26 PM 1
Share

Done! Your answer was also very helpful, thanks again @$$anonymous$$outon

avatar image
3
Wiki

Answer by Mouton · Jul 07, 2015 at 11:13 AM

Please refer to @Bunny83's solution since my solution is mostly based on his corrections

For unconstrained generic types, you can't test if the value is null since you don't know if the type is nullable.

You have 2 solutions, depends of what you try to achieve. First is to constraint your generic type to be derived of a nullable type, such as Object:

 public T[] Get<T> (int x, int y) where T : class
 {
     // [...]
 }

Another solution is to check for the default value instead a null value:

 foreach (T t in test)
     if (IsNullOrFakeNull(t))
         Debug.Log("yo");
 
 public static bool IsNullOrFakeNull(object aObj)
 {
     return aObj == null || aObj.Equals(null);
 }

[Edit] Edited the second solution to avoid boxing

[Edit]

  • Changed the first solution constraint to "class"

    The "class" constraint just limits the use to reference types.

  • Added @Bunny83's fake null test in the second solution.

Comment
Add comment · Show 10 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bonfire-Boy · Jul 07, 2015 at 12:17 PM 1
Share

It's still hard to say without knowing what GetRange() does, but it really necessary to use generics here? $$anonymous$$y gut feeling, seeing as the Get() function is so simple, is that you could make things a lot easier for yourself by just writing a version of that function that's specific to Tiles.

avatar image Mouton · Jul 07, 2015 at 12:34 PM 1
Share

I edited the solution, please use

 if (t.Equals(default(T)))

ins$$anonymous$$d

avatar image Bunny83 · Jul 07, 2015 at 12:51 PM 1
Share

@$$anonymous$$outon:
That makes no sense. Your "null check" only works for Unity's fake null objects. For any real null reference this will throw a null-reference exception. The Equals method is an instance method.

I just want to stress that: Do not use Equals for null checks, ever.

avatar image Mouton · Jul 07, 2015 at 01:13 PM 1
Share

@Bunny83 Why is it a bad idea to use "Object" as a generic constraint ?

Will this not work in Unity ?

 if (EqualityComparer<T>.Default.Equals(t, default(T)))
 {
     Debug.Log ("Not null");
 }
avatar image Bunny83 · Jul 07, 2015 at 04:04 PM 2
Share

@$$anonymous$$outon:
I never said you couldn't use Object as generic constraint. Btw you didn't mention if you ment UnityEngine.Object or System.Object. System.Object can't be used as constraint since everything is compatible with it the compiler doesn't allow it.

When choosing a constraint you always limit the useage of a generic class / method and on the other hand provide more functionality inside the generic class / method since the type is more specific. A constraint should always be as general as possible and as restrictive as necessary.

From what we know the generic parameter is used with a type called "Tile" and this seems to be a reference type since he wants a null check (which is pointless for value types). However we don't know from which class it might be derived from. The "class" constraint just limits the use to reference types.

About your second question: EqualityComparer uses Object.Equals unless the class in question implements IEquatable<T> which none of the Unity classes do.

However it seems that $$anonymous$$ono's implementation is slightly different from microsofts. This is the Equals method of the default comparer for object types:

 // microsoft's implementation
 public override bool Equals(T x, T y)
 {
     if (x != null)
     {
         return y != null && x.Equals(y);
     }
     return y == null;
 }
 // $$anonymous$$ono's implementation
 public override bool Equals(T x, T y)
 {
     if (x == null)
     {
         return y == null;
     }
     return x.Equals(y);
 }

At first glance they look equal except that they have reversed logic. However $$anonymous$$ono has one null check less. In the case where it uses Equals it doesn't check y for null which would allow to catch Fake-null objects, but only when you pass the reference first and "null" last. If you reverse the two parameters it doesn't work anymore ^^

Btw System.Object.Equals is implemented like this:

 public static bool Equals(object objA, object objB)
 {
     return objA == objB || (objA != null && objB != null && objA.Equals(objB));
 }

For Unity classes if you want reliably catch if they are fake null or null you first have to do a normal null check and then when you're sure that it's not null, use Equals. If that returns true it's "fake null".

I posted this somewhere else already. You can use something like this:

 public static bool IsNullOrFakeNull(object aObj)
 {
     return aObj == null || aObj.Equals(null);
 }

This will return true even for fake null objects.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

24 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Unity event calling function gets nullreferencexception on bool 0 Answers

Arraylist to GameObject[] array issue. 1 Answer

How to pass objects through array? 1 Answer

NullReferenceExeption when trying to put GameObject into an Array 1 Answer

NullReferenceException: Object reference not set to an instance of an object player.Update () (at Assets/player.cs:10) 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges