为什么在非 MonoBehaviour 序列化 class 中调用多个构造函数?

Why multiple constructor calls in a non MonoBehaviour serialized class?

我已将脚本附加到 Unity 游戏对象。该脚本包含各种 public 属性,包括我自己的一些 类。就像在下面的简化代码中一样,其中 TestMonoBehaviorClass 附加到游戏对象并且 TestClass' TestString 显示在检查器中。

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";
    }
}

我希望 TestClass 的构造函数(编辑:不是派生自 MonoBehavior 的构造函数被调用一次,当我将脚本附加到游戏时目的。但是如果我在 Unity 编辑器中 运行 程序然后停止程序,它会被调用四次。如果我将脚本附加到两个游戏对象,则可以执行七次。至少我在控制台中多次看到 Debug.Log 的输出。

不过,如果我在编辑器中更改 TestString 属性 的内容,我手动输入的内容不会被覆盖!

为什么经常调用构造函数? Unity的执行顺序是什么时候调用的(Unity's execution order of event functions)? 我可以忽略调用,还是必须在我的构造函数中添加特殊处理?到目前为止,我没有看到任何实际问题或副作用。

编辑: 好像只有没有参数的构造函数被调用了。如果我只有带参数的构造函数,那么会调用 none。

您不应在 Unity 中使用构造函数,而是使用 Awake 函数来赋值,否则您可以创建自己的 setter 函数来设置值。

检查这个:http://answers.unity3d.com/questions/862032/c-constructor-in-monobehaviour.html

[System.Serializable] 是多次调用构造函数的原因。忽略它,除非因此导致错误,然后询问有关该特定错误的问题以获得变通解决方案。如果删除 [System.Serializable],Unity 将不会序列化 TestClass class 并且构造函数将被调用 一次 而不是多次。

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

使用[System.Serializable],Unity序列化会在序列化和淡化时多次调用默认构造函数,因为反序列化时需要重新创建Object。构造函数用于执行此操作。有关 Unity 中其他类似的多构造函数调用问题,请参阅 post。

编辑:

如果你想在序列化完成之前或之后做一些事情,你可以分别实现 ISerializationCallbackReceiver interface and use the OnBeforeSerialize() or OnAfterDeserialize() 函数来完成。

例如:

[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);
    }
}