使用monobehaviour的构造函数

时间:2013-10-03 18:12:50

标签: c# unity3d readonly

好的,我知道为什么我们不在Unity中的单一行为上使用构造函数。对于几乎所有的用例,Start和Awake完美契合。一般

但是,有一个很棒的C#功能只能用于构造函数 - 只读字段。在我的特殊情况下,我与许多开发人员合作开发一个项目并编写一个抽象的MonoBehavior,它将被许多不同的人进行子类化和重写。而且我希望一个字段在整个对象的生命周期中表现得像常量(或者它会引入奇怪的,难以检测的错误)但在不同的子类中具有不同的值 - 换句话说,一个用于readonly字段的经典用例。 (我不想使用属性,因为他们没有保持相同的语言强制义务。)

那么 - 我可以安全地使用MonoBehavior的构造函数吗?不会有一些奇怪的龙从路上的某个地方出来吗?如果我选择使用它们应该知道什么?

3 个答案:

答案 0 :(得分:2)

我认为Unity希望您远离使用构造函数的主要原因是在主线程上没有调用构造函数,并且在序列化数据恢复到对象之前调用构造函数。

因此,如果您在构造函数中设置的只读字段依赖于序列化字段中的数据,那么它们将无法正常工作。如果他们不这样做,那么你可以在初始化时分配它们。

您也可以使用容器对象来保存您的只读值,但是没有什么可以阻止其他人稍后重新分配该容器。

using UnityEngine;
using System.Collections;

public class ReadOnlyTest : MonoBehaviour {

    public string part1 = "alpha";  // change these values in the editor and 
    public string part2 = "beta";  // see the output of the readonly variable "combined"

    public readonly string combined;

    // just assign to readonly vars.
    public readonly string guid = System.Guid.NewGuid().ToString();
    public readonly float readOnlyFloat = 2.0f;


    public class ReadOnlyContainer {
        public readonly int readOnlyInt;
        public readonly float readOnlyFloat;
        public readonly string readOnlyString;

        public ReadOnlyContainer(int _int, float _flt, string _str) {
            readOnlyInt = _int;
            readOnlyFloat = _flt;
            readOnlyString = _str;
        }

        public override string ToString() {
            return string.Format("int:{0} float:{1} string:{2}", readOnlyInt, readOnlyFloat, readOnlyString);
        }
    }

    public ReadOnlyTest() {
        combined = part1 + part2;
    }

    public ReadOnlyContainer container;

    void Awake() {
        if (container == null) {
            container = new ReadOnlyContainer(Random.Range(-100,100), Time.realtimeSinceStartup, System.Guid.NewGuid().ToString());
        }
    }

    void Start () {
        Debug.Log(container.ToString());

        Debug.Log("combine1: " + combined);

        Debug.Log("guid: " + guid);
    }   
}

答案 1 :(得分:1)

许多统一类是通过反射创建的,并且没有办法正确地统一到非默认构造函数;因此有限制。

@Calvin的回答指出了一个非常好的选择:创建不是从MonoBehaviour派生的类;这些可以像任何其他C#一样具有构造函数。只要您的代码可以容忍丢失的实例,您就可以将这些类放入MonoBehaviours的字段中。如果你使用来自@ Calvin回答的典型准单一模式,那么当你需要一个实例时,你总会得到一个实例,并且你可以在第一时间给我一个实例'逻辑到一个方法,可以在派生类中重写以自定义行为。

如果你想要类似于常量的行为,在派生类中选择不同的值,可能更容易定义方法而不是字段。该方法实际上是只读的,根据@ Jerdak的答案,它具有更多可预测的突变。

如果你必须有构造函数,最后一个选项是使用monobehavior作为最小占位符并在你自己的类中编写所有有趣的东西,然后将Monobehavior中的所有工作委托给你的类。

using UnityEngine;
using System.Collections;

public class OuterPlaceholder: MonoBehaviour {

      public InnerBehavior _Inner;
      public void Awake() {
           if (_Inner == null) {
           _Inner= new InnerBehavior(4);
        }
    }

     public void Update()
     { 
        _Inner.DoUpdate(this);
     }

}

public class InnerBehavior 
{
     public readonly int UpConstant;
     public InnerBehavior (int up)
     {
     UpConstant = up;
     }

     public void DoUpdate(MonoBehaviour owner)
     {
      owner.transform.Translate(Vector3.up * UpConstant * Time.deltaTime);
     }

}

如果您确定在项目发展过程中获得大量复杂的继承,则此选项可能效果最佳。

最后:将字段_ReadOnlyField或_DoNotWrite或其他任何内容命名为不要告诉用户不要捣乱它是完全可以的。所有Python程序员都有可能让某人做得更糟糕,而且大部分时间似乎都很好:)

答案 2 :(得分:0)

来自script refs

  

如果您尝试为脚本组件定义构造函数,它将会   干扰Unity的正常运行并可能导致重大问题   该项目存在问题。

MonoBehaviours在序列化过程中被多次构建,Unity在编辑器中经常使用,我怀疑还有很多东西可以将C层挂钩到C#。最终行为是不确定的,所以最好不要尝试。

关于“但在不同子类中使用不同的值”,from MSDN

  

声明[readonly]引入的字段的赋值只能作为声明的一部分或同一类的构造函数中出现。

所以在派生类中没有修改。