Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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 BadAssGames · Apr 03, 2015 at 01:20 AM · movementnullreferenceexceptionserializationsavefield

Using reflection, sometimes a field is "null" and sometimes it is "Null"?

I've been writing a generic serialization system over the past few days and it's getting close to complete right now. I put in a null check to make sure no empty fields are passed in (and thus don't need to be serialized) and I noticed a result that I don't understand.

I have a sample script named TestScript which looks like this:

 public class TestScript : MonoBehaviour 
 {
     public Texture2D texture;
     public Material mat;
     public GameObject gobj;
     public OtherScript other;
 }

When I send this to the serializer, I get all the fields off of TestScript using FieldInfo. I then check to see if these field info's are null. This is the comparer:

         //field is a FieldInfo. We get the value from an instance that it exists on. In This case target.
        object obj = field.GetValue(target);//Get the value of this field on our target script
        Debug.Log(obj);
        if(obj.Equals(null))
        {
             return;
        }

When comparing the Texture2D, the Material, and the GameObject, I get a return type of "null". When I try to compare the class OtherScript, I get a return type of "Null". The "Null" return throws a NullReferenceException at this line and the others don't. Why is this and how can I check to make sure the field OtherScript is not null?

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 joao-cabral · Nov 18, 2016 at 04:23 PM -1
Share

@BadAssGames Did you solve this thing? I'm getting the same "null" reference on Unity native components.

2 Replies

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

Answer by Bunny83 · Apr 03, 2015 at 03:06 AM

I'm pretty sure you just stumbled on that little "null trick" Unity implements for all objects derived from UnityEngine.Object.

As you might know objects derived from UnityEngine.Object usually have a native counterpart on the C++ side of the engine. Since C# uses a managed memory space and C++ not there are some problems with this object "duality". In C++ (and Unity) you can actually "destroy" / free an object manually. In C# that's not possible since the managed memory is released when all references to an object go out of extent.

Now we have the method Destroy which can destroy all objects derived from UnityEngine.Object. So how does this work in the managed environment? The answer is: it actually doesn'T work. It's not possible to destroy a managed object. When you call Destroy on a Component Unity just destroys the native part on the C++ side. The managed object is still there until all references to that object are gone.

Unity uses a little "trick" to make a reference to such a "dead object" appear null by simply overloading the Equals method and == operator. So when you compare the reference to null they will return true, even the reference isn't null. Though since the object is actually dead you can't use it anymore.

The same thing happens to MonoBehaviours objects that never had a native part. This happens when you create an instance of a MonoBehaviour with the "new" keyword. This will create a managed object, but since the component isn't attached to a GameObject it's in it's dead state from the very beginning.

So first rule No1: Never use the new keyword on MonoBehaviour derived classes.

btw:
This comparison makes no sense at all:

 if(obj.Equals(null))

If obj is actually null this will throw a NullReferenceException since you can't call Equals on a null object. It's possible to call it on a "fake-null" object since you actually have an object.

Since the == operator is not a virtual method you will notice a difference when using a variable of a type derived from UnityEngine.Object and a variable of type System.Object:

 SomeMonoBehaviour A = new SomeMonoBehaviour();  // create fake null object
 
 Debug.Log("A == null:  " + (A == null));
 Debug.Log("A.Equals(null) :  " + (A.Equals(null)));
 
 object B = A;  // assign the same reference to an System.Object variable
 
 Debug.Log("B == null:  " + (B == null));
 Debug.Log("B.Equals(null) :  " + (B.Equals(null)));

The result will be:

 "A == null:  true"
 "A.Equals(null) :  true"
 "B == null:  false"
 "B.Equals(null) :  true"

So knowing that little fact you can test a "seamingly" null reference for being fake null by casting it into "object" do a == check for null and an Equals check afterwards like this:

 bool IsReferenceFakeNull(object aRef)
 {
     return aRef != null && aRef.Equals(null);
 }


This method is safe for any reference as true null values won't trigger the Equals check which would cause a null-ref-exception otherwise.

Comment
Add comment · Show 4 · 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 BadAssGames · Apr 03, 2015 at 03:13 AM 0
Share

Just for clarity, this (for me) was happening with Unity native components (Texture2D, $$anonymous$$aterial, GameObject, and probably any other Unity specific classes). They would return as "fake-nulls".

avatar image Bunny83 · Apr 03, 2015 at 03:27 AM 0
Share

@BadAssGames: That's not possible unless you actually used Destroy on them or they got destroyed in another way.

Example:

 var A = new Texture2D(10,10);
 DestroyImmediate(A);
     
 Debug.Log("A == null:  " + (A == null));
 Debug.Log("A.Equals(null) :  " + (A.Equals(null)));
         
 object B = A;
         
 Debug.Log("B == null:  " + (B == null));
 Debug.Log("B.Equals(null) :  " + (B.Equals(null)));

With the DestroyImmediate call the texture reference will become a fake-null object. When you comment it out all 4 null checks will return false as it's a valid object.

avatar image BadAssGames · Apr 04, 2015 at 04:56 AM 0
Share

Yeah I'm not sure what's happening here. But it's definitely still returning as I described above and not in the manner that you said it should. Basically all I'm doing is this:

 object instance = field.GetValue(target); //Gets an instance of Texture2D from the target script. There is no Texture2D to get, so the value it should get is null
 
 if(instance == null)<--- Will never return true
 {
     Debug.Log("No Texture2D is stored there!");
 }

I don't know why this is. I'm not calling Destroy or DestroyImmediate anywhere. I'm not creating a new $$anonymous$$onobehaviour (unless maybe GetValue is doing this for me?)

avatar image Bunny83 · Apr 06, 2015 at 03:59 PM 0
Share

@BadAssGames: I just made a few tests and i think i know what happens in your case. If you have a unity-serialized class ($$anonymous$$onoBehaviour or custom serialized class as part of a $$anonymous$$onoBehaviour) that contains a reference to a Texture2D which hasn't been setup, Unity creates a "fake" object and assigns that one to the variable when the class gets deserialized. This fake object also pretends to be null and is responsible for those error messages:

"UnassignedReferenceException:The variable tex of SerializationTests has not been assigned.\nYou probably need to assign the tex variable of the SerializationTests script in the inspector."

avatar image
0

Answer by DiegoSLTS · Apr 03, 2015 at 01:40 AM

What does "GetValue" do?

Your null check is a little weird, if obj is actually "null" then you can't all a method ("Equals") on the object. Also, if the object is null you are trying to call the ToString method on it that, again, can't be called since the object is null at that point.

To check if a variable is null you should use:

 if (obj == null) {
     Debug.Log("obj is null"); //you can't call ToString on a null pointer
     return;
 }

For the class OtherScript your GetValue function is returning a null pointer, that's why your code crashes. For the rest, did you ever had one of the values actually printing that Debug.Log message? If you had, then the method it's returning... something else, I'm not sure what could it be, but it's not a "null", is something else that has a ToString method that returns "null" when called, and a redefinition of Equals that returns true when null is passed.

Comment
Add comment · Show 1 · 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 BadAssGames · Apr 03, 2015 at 02:32 AM 0
Share

I've edited my original post to be a little more clear about "field". the field variable in this case is a Field Info. I use field.GetValue to return an actual instance of on the target script (which in this case is an instance of the TestScript class).

Using obj == null does not work in this case. It will not return true on my script.

$$anonymous$$y console readout, when running "TestScript" through my serializer says the following about each variable:

 null  <--- Texture2D
 null  <---- $$anonymous$$aterial
 null  <--- GameObject
 Null  <---- OtherScript
 NullReferenceException etc. etc.


ETA: So, after some further testing. It seems you're very much correct. When trying to check the Texture2D, $$anonymous$$aterial, and GameObject, only obj.Equals(null) will work. However, when trying to test the Otherscript object, obj == null will work. I don't understand why this is, however. Is there a catch-all solution I can use that will ensure I accurately check if a value is null?

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

21 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

Related Questions

Save EditorWindow to disk 1 Answer

Save multiple scriptableObjects in just one binary file 2 Answers

Error when loading saved date : SerializationException: End of Stream encountered before parsing was completed. 2 Answers

Unity 2D C# - Save And Load Data 1 Answer

JSON - Load Subset of Files Based on Key in File 0 Answers


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