Search code examples
c#unity-game-engineconstructorexecutioninspector

Why multiple constructor calls in a non MonoBehaviour serialized class?


I have attached a script to a Unity game object. The script contains various public properties, including some of my own classes. Like in the following simplified code, where the TestMonoBehaviorClass is attached to a game object and the TestClass' TestString is shown in the inspector.

public class TestMonoBehaviorClass : MonoBehaviour
{
    public TestClass Test;
}

[System.Serializable]
public class TestClass
{
    public string TestString;
    public TestClass ()
    {
        Debug.Log ("TestClass creator called.");
        this.TestString = "Reset String";
    }
}

I would expect that the constructor of the TestClass (Edit: NOT the one derived from MonoBehavior) is called once, when I attach the script to the game object. But it is called four times if I run the program in the Unity editor and then stop the program. Seven times if I have the script attached to two game objects. At least I see the output of the Debug.Log in the console that many times.

Still, if I change the content of the TestString property in the editor, the content I enter manually is NOT overwritten!

Why is the constructor called that often? When is it called in Unity's execution order (Unity's execution order of event functions)? Can I kind of ignore the call, or do I have to add special handling into my constructor? So far I didn't see any actual problem or side-effect.

Edit: It seems that only constructors without parameters are called. If I only have constructors with parameters, then none is called.


Solution

  • [System.Serializable] is why the constructor is being called multiple times. Just ignore it unless there is a bug that results because of that, then ask question about that specific bug for a work-around solution. If you remove [System.Serializable], Unity won't serialize the TestClass class and the constructor will be called once instead of multiple times.

    Edit: It seems that only constructors without parameters are called. If I only have constructors with parameters, then none is called.

    With [System.Serializable], Unity serialization will call the default constructor multiple times during serialization and desalination because it needs to recreate the Object when it is de-serialized. The constructor function is used to do this. See this post for other similar multiple constructor call issue in Unity.

    Edit:

    If you want to do stuff before or after the serialization is done, you can implement ISerializationCallbackReceiver interface and use the OnBeforeSerialize() or OnAfterDeserialize() function to do this respectively.

    For example:

    [System.Serializable]
    public class TestClass : ISerializationCallbackReceiver
    {
        public string TestString;
        public TestClass()
        {
            Thread thread = Thread.CurrentThread;
            Debug.Log("TestClass creator called: " + thread.ManagedThreadId);
    
            this.TestString = "Reset String";
        }
    
        public void OnAfterDeserialize()
        {
            Thread thread = Thread.CurrentThread;
            Debug.LogWarning("OnAfterDeserialize Thread ID: " + thread.ManagedThreadId);
        }
    
        public void OnBeforeSerialize()
        {
            Thread thread = Thread.CurrentThread;
            Debug.LogWarning("OnBeforeSerialize Thread ID: " + thread.ManagedThreadId);
        }
    }