Unity 基底クラスのAwake()などを呼び出す

 Unity5.1.0&C#&VisualStudio2013でのおはなしです。

 大した事でもないですが、Unityを使っていて気になったので書いておきます。Unityの話というよりC#の話のような気がしますが……


 あるクラスHageが、MonoBehaviourを継承したクラスSuperHageを継承しているとします。
 SuperHageにはAwake()が定義されており、HageにもAwake()を定義しました。以降、HageクラスのAwake()をHage.Awake()、SuperHageクラスのAwake()をSuperHage.Awake()と書くことにします。


/*SuperHage.cs*/
class SuperHage : MonoBehaviour{
    void Awake(){
        Debug.Log("すごくハゲた。");
    }
}

/*Hage.cs*/
class Hage : SuperHage{
    void Awake(){
        Debug.Log("ハゲてきた。");
    }
}


 次に、Hageスクリプトをヒエラルキー内のゲームオブジェクトにアタッチします。

 
 そして、コンストラクタがスーパークラスのコンストラクタを呼び出すのと同じように、SuperHage.Awake()とHage.Awake()の両方が動くことを期待して動かしてみました。が出力は……

 「ハゲてきた」
のみです。

 両方呼び出されると、「ハゲてきた」と「すごくハゲた」が出力されるはずですが、実際はHage.Awake()しか呼び出されなかったということになります。考えてみると、ゲームオブジェクトにアタッチされているのはHageクラスのため、当然です。

 そのため、SuperHage.Awakw()も呼び出したい場合はHage.Awake()内で明示的に呼び出さなければなりません。

 C#では基底クラスにbaseでアクセスできるため、以下のように書けます。

 base.Awake();

しかしこのままだとSuperHage.Awake()にアクセスできないと言われ、エラーになります。これはSuperHage.Awake()にアクセス修飾子を指定していないことで、デフォルトのprivateになっているからです。

 そこでSuperHage.Awake()をprotectedにして再挑戦してみます。が警告が出てしまいました。

「'Hage.Awake()' は継承されたメンバー 'SuperHage.Awake() を隠します。現在のメンバーでその実装をオーバーライドするには、override キーワードを追加してください。オーバーライドしない場合は、new キーワードを追加してください。」


 派生クラスHageで、基底クラスSuperHageに存在するAwake()という関数と同名の関数を定義したため、親クラスのAwake()は隠される(呼び出せなくなる)という警告です。

 基底クラスで定義されている関数と同名の関数を派生クラスで定義すると、基底クラスの案数は隠ぺいされますが、それが故意ではない場合、思わぬバグをもたらすので警告が出るのでしょう。


 これを解消するためには二つ方法があります。

 まず、基底クラスSuperHageのAwake()をvirtualで仮想関数にし、派生クラスHageのAwake()にはoverrideを指定して、SuperHage.Awake()をオーバーライドするようにします。

 または、Hage.Awake()にnew修飾子をつけ、隠ぺいが故意のものであることを明示します。(このnewはオブジェクト生成時のnewとは別物です)

 こうすることで先ほどの警告を消すことができ、基底クラスのAwakeも呼び出せるようになりました。最終的に以下のコードになり、「すごくはげた。はげてきた。」と出力されます。コードは上で挙げたそれぞれの方法について別々に書いておきました。


 基底クラスの関数をオーバーライドする方法

/*SuperHage.cs*/
class SuperHage : MonoBehaviour{
    protected virtual void Awake(){
        Debug.Log("すごくハゲた。");
    }
}

/*Hage.cs*/
class Hage : SuperHage{
    protected override void Awake(){
        base.Awake(); //Superhage.Awake()を呼び出す。
        Debug.Log("ハゲてきた。");
    }
}


 new修飾子で基底クラスの関数を明示的に隠ぺいする方法

/*SuperHage.cs*/
class SuperHage : MonoBehaviour{
   protected void Awake(){
        Debug.Log("すごくハゲた。");
    }
}

/*Hage.cs*/
class Hage : SuperHage{
   new void Awake(){
        base.Awake(); //Superhage.Awake()を呼び出す。
        Debug.Log("ハゲてきた。");
    }
}



 例で使ったクラス名でハゲやらスーパーハゲやら言っていましたが……最近本気で生え際が気になってきたんですけど……まだ20歳なのに……
スポンサーサイト




  • テーマ:プログラミング
  • ジャンル:コンピュータ
  • コメント

    コメントの投稿

    非公開コメント

    プロフィール

    Cdec

    Author:Cdec
    情報系修士出身のIT企業会社員。趣味で変なゲームを作ったり、気まぐれにゲームしたりしています。
    研究ではDeepLearning関連のことをしていました。

    ■メインPC
    Win10Pro, Corei7-9700K, DDR4 16GB, SSD 256GB, HDD 2TB*2, RTX2060
    ■サブPC
    Win10Pro, Corei5-8250U, DDR4 8GB, SSD 256GB
    ■サブサブPC
    Win10Pro, Corei5-3337U, DDR3 4GB, SSD 256GB
    ■ファイルサーバー
    WinServer2016Standard, DDR4 4GB, Corei5-6500, SSD 128GB, HDD 750GB*2+1TB
    ■開発環境&ライブラリ
    ゲーム開発ではVisualStudio, Unity, DXライブラリ、研究ではVisualStudioCode, Caffe, Chainer
    ■使う言語
    ゲーム開発ではC++やC#、たまにLuaで、研究ではPython