2013-10-03 113 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程序員都生活在有人做更糟糕的事情的可能性,似乎大部分時間工作得很好:)

相關問題