【C#】【Unity】継承とInterfaceに触れてみた
はじめに
UnityとC#を再学習中です。 さんざんコード書いておいて今更感はありますが、前から気になっていた継承とInterfaceについて調べてみました。
以前も使用した、Assets以下に置いた画像ファイルを「ReadAllBytes」と「LoadImage」を使って、
マルチスレッドで読み込もうとしたら見事に失敗したので、その腹いせでもありますw
(MonoBehaviourを継承していなければマルチスレッドが使用できるのですが、「LoadImage」はメインスレッドでしか動かないとのことでした。これについては継続してうまい方法を考えてみます)
最後に参考リンクをまとめますが、継承もInterfaceも、ググれば有用な情報がたくさん手に入ります。 なので、ここではこの2つの要素について、なんとなく私が理解した内容や、(Unityで)自分ならこう使うかな、ということをまとめておきたいと思います。
継承
まずは継承から。親となるクラスの継承やメソッド、メンバ変数をそのまま引き継ぐことができます。
ParentBase.cs
using UnityEngine; public class ParentBase : MonoBehaviour { private int intPersonalNum = 0; public void Output() { Debug.Log(intPersonalNum); Debug.Log(this.GetRect()); } private Rect GetRect() { var rctNewRect = new Rect(0, 0, 320, 240); return rctNewRect; } }
この親クラスを継承します。
CtrlChild1.cs
using UnityEngine; public class CtrlChild1 : ParentBase { public void OutputOnCtrlChild1() { // 独自メソッド. Debug.Log("Child1"); } }
で、これをメインクラスから呼び出してみます。
CtrlMain.cs
using UnityEngine; public class CtrlMain: MonoBehaviour { private CtrlChild1 ctrChild1; public GameObject gmoChild1; private void Start() { ctrChild1 = gmoChild1.GetComponent(); // ParentBaseから継承したメソッドの呼び出し. ctrChild1.Output(); // 独自メソッドの呼び出し. ctrChild1.OutputOnCtrlChild1(); } }
CtrlChild1.csでは実装していないメソッドや変数も、継承元のParentBase.csで実装しているため呼び出すことができます。
親クラスのメソッドをオーバーライドして、独自のふるまいをさせることも可能です。
ParentBase.cs
~省略~ // 子クラスでオーバーライドさせるためにはvirtualやabstractなどの修飾子を付与する必要がある. public virtual void Output() { Debug.Log(intPersonalNum); Debug.Log(this.GetRect()); } ~省略~
CtrlChild1.cs
~省略~ public override void Output() { // 親クラスのメソッドをオーバーライドする. Debug.Log("Overridden"); } ~省略~
使い方
UnityEditorからC#スクリプトを作成すると、標準で「MonoBehaviour」を継承したクラスが作成されるので、ある意味お馴染み?
親クラスで「MonoBehaviour」を継承しておけば、子クラスも通常通りGameObjectにアタッチできます。
複数のScriptで、全く同じメソッドや処理をさせたいとき(はっきりと思いつかないのですが。。。)に使えそうです。
また、上記のCtrlMain.csでの呼び出し時、「private CtrlChild1 ctrChild1;」ではなく親クラスの「private ParentBase ctrChild1;」としても、同じようにCtrlChild1.csをアタッチして使用することができます。
(※ただし子クラスで独自実装したメソッドは使用不可)
下記のInterfaceとともに、例えば実行中のSceneによってほぼ同じ内容で微妙に異なるScriptを使いたい場合、以下のようにできる、ということですね。
private ParentBase ctrChild1; private ParentBase ctrChild2; private ParentBase currentChild = (Application.loadedLevelName == "Scene1")? ctrChild1: ctrChild2;
呼び出し元のScriptが一つにまとめられるだけでも、少しスッキリする気がします。
Interface
こちらは親を子クラスが継承する、というところは同じですが、いくつか特徴があります。
- 複数の継承が可能(継承の場合は1つのみ)
- Interface自身はクラスを継承することはできない
- Interface自身は実装を持たない
- 子クラスはInterfaceが持つメソッド、プロパティなどをすべて実装する必要がある
- メソッドやプロパティなどはすべてpublicかつabstractとして扱われる
Iparent.cs
interface Iparent{ void Initialize(); void UpdateStatus(); string StrRoleName { get; set; } }
CtrlChild2.cs
using UnityEngine; public class CtrlChild2 : MonoBehaviour, Iparent { void Start () { } void Update () { } public void Initialize() { Debug.Log("Child2 init"); } public void UpdateStatus() { Debug.Log("Child2 update"); } public string StrRoleName { get; set; } }
使い方
Interface自身はクラスを継承できないこと、また子クラスは複数のInterfaceを継承できることから、「MonoBehaviour」も継承し、GameObjectにアタッチできるようにしています
また、メソッドの実装部分は子クラスごとに行われるため、継承のところで書いた「実行中のSceneによってほぼ同じ内容で微妙に異なるScriptを使いたい場合」はInterfaceを使って、 「Iparent」を型として子クラスを呼び分けたいときに使えそうです。
C# 実践開発手法ではクラス間の依存関係を取り除くため、Interfaceが使われていました。
今回は触れられませんでしたが、次回以降試してみたいと思います。
終わりに
まだぼんやりした理解でしかありませんが、うまく使いこなしてより簡潔にコードが書けるようになりたいものです(小並感)