2013-10-03 115 views
1

好的,我知道为什么我们不在Unity中的monobehaviors上使用构造函数。对于几乎所有的使用案例来说,Start和Awake都完美契合。通常。使用单行程的构造函数

但是,有一个很棒的C#特性只能用于构造函数 - 只读字段。在我的特殊情况下,我和很多开发人员一起开发了一个项目,并撰写了一个抽象的MonoBehavior,这个抽象的MonoBehavior将被很多不同的人分类和重写很多次。我希望字段在整个对象的整个生命周期中都像常量一样工作(或者它会引入奇怪的,难以察觉的错误),但在不同的子类中使用不同的值 - 换句话说,就是只读字段的经典用例。 (我不想使用属性,因为他们没有保持相同的语言执行义务。)

所以 - 我可以安全地使用MonoBehavior的构造函数吗?难道一条陌生的龙不会从这个巢穴的某个地方出来吗?如果我选择使用它们,我应该知道什么?

回答

1

我认为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); 
    } 
} 
0

script refs

如果试图定义脚本组件构造函数,它将 干扰团结的正常运行,并可能导致重大 问题的项目。

MonoBehaviours被序列化过程中建造了许多次,团结的东西确实相当频繁在编辑器中,我怀疑是有很多事情引擎盖挂钩C层为C#。最终,行为是不确定的,所以最好不要尝试。

关于“但在不同的亚类不同的值”,from MSDN

分配到由该声明引入的字段[只读]只能发生作为声明的一部分或在同一类的构造。

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

1

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

@卡尔文的回答指出了一个非常好的选择:创建不是从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程序员都生活在有人做更糟糕的事情的可能性,似乎大部分时间工作得很好:)

相关问题