1

わかりました。Unity でモノビヘイビアーにコンストラクターを使用しない理由はわかっています。ほぼすべてのユース ケースで、Start と Awake は完全に適合します。いつもの。

ただし、コンストラクターでのみ使用できる優れた C# 機能、つまり読み取り専用フィールドがあります。私の特定の状況では、多くの開発者と一緒にプロジェクトに取り組んでおり、多くの異なる人々によって何度もサブクラス化および書き換えられる抽象的な MonoBehavior を作成しています。そして、フィールドがオブジェクトの存続期間を通じて一定のように動作するようにしたい (そうしないと、奇妙で​​検出しにくいバグが発生する) が、サブクラスごとに異なる値を持つようにする必要があります。つまり、読み取り専用フィールドの古典的な使用例です。(プロパティは、同じままであるという言語強制の義務がないため、使用したくありません。)

では、MonoBehavior のコンストラクターを安全に使用できますか? どこかの隠れ家から変なドラゴンが出てくるんじゃないの?それらを使用することを選択した場合、何を知っておくべきですか?

4

3 に答える 3

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);
    }   
}
于 2013-10-03T20:31:02.360 に答える
1

多くのユニティ クラスはリフレクションによって作成されます。ユニティをデフォルト以外のコンストラクタに適切に適用する方法はありません。したがって、制限。

@Calvin の回答は、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 プログラマーは、誰かがもっと悪いことをする可能性を抱えていますが、ほとんどの場合はうまくいくようです :)

于 2013-10-04T05:03:37.607 に答える