C#で継承を使ってみる

今回はC#で継承を使ってみたいと思います。

手続き型のプログラミング言語から始めた人は苦手意識を持っている人も多いのではないでしょうか?

わたくしも苦手ですね。(^_^;)

今回は継承とは何か?といったところから、インタフェース、抽象クラスを作成し、それらを継承して新たなクラスを作成してみようと思います。

スポンサーリンク

継承とは?

継承とはいったいなんでしょうか?何の為に継承という機能があるんでしょうか?

まずはそれを考えてみます。

例えばキャラクタークラスを作成するとして、キャラクターは名前、性別、攻撃力、防御力、特技などのパラメータやそれらを操作するメソッドが必要になります。

それらをCharacterという名前のクラスに全部詰め込んで定義する事も出来ます。

しかしこのキャラクターとは別のキャラクターを作成しようと思った時に一部のパラメータや操作メソッドが違う為にCharacter2クラスを作成する事になりました。

さらに、また別のキャラクターも作る事になり合計100の違うクラスを作成する必要が出ました・・・・となったら100人それぞれパラメータやメソッドが微妙に変化するほとんど同じクラスを100個作成する事になります。

ほとんど同じクラスなのに別に100個のクラスを作成するなんて面倒ですし、中身を把握するのもそれぞれ把握しなければならない為、管理が大変です。

また処理の内容を変更しようと思ったら100個のクラス全てに修正を入れていく必要があります。・・・( ・_;)( ;_;)( ;_;)

そんな時に継承の機能を使えば一部のパラメータやメソッドだけを修正するだけで実現出来ます。

クラスで共通するパラメータや操作を抜き出して、それを継承する形でそれぞれのクラスを作成する。

とすれば、同じパラメータや同じ操作をクラス毎に記述する必要がなくなりますし、修正が入っても共通のクラスは1つのクラスを修正するだけで終わりですね。(^^)/

そんなわけで継承とはクラスで共通するクラスを纏めて別のクラスにし、そのクラスの機能を受け継いだ新しいクラスを作成するということですね。

継承元はスーパークラス、親クラス、基底クラス、継承先はサブクラス、子クラス、派生クラスと呼ばれます。

親クラスになればなるほど機能が抽象化されていき、子クラスになればなるほど具象化されたクラスとなります。

子クラスから親クラスのフィールドやメソッドを使う事が出来る上に、子クラスで新しく定義したフィールドやメソッドも使えるというわけです。

これは便利ですね!

継承をする時の注意点

継承をする時にはある決まりを持って継承を行うかどうかを決める必要があります。

子クラスは親クラスの一種である必要があります。

例えば親クラスをHumanクラス、子クラスをMasashiとした場合はMasashiは人間なので継承は可能。

という風に、現実の世界で子クラスは親クラスの一種である必要があります。

親クラスをHuman、子クラスをPenとした場合、ペンは人間の一種ではありませんので、継承は使わないという感じです。

継承なんて使わずにプログラミングしたい!

継承はよくわからないから継承を使わずにプログラミングしてやる!

なんて方もいるかもしれませんが、実はこれ最初から出来ません。(^_^;)

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/object

すべての型 (定義済み、ユーザー定義、参照型、および値型) が、直接または間接的に Object を継承します。

なのでクラスを定義した時点ですでにObjectクラスを何らかの形で継承しているんですね。

Unityでスクリプトを作った場合もデフォルトではMonoBehaviourクラスを継承してスクリプトが作られています。

例えばC#スクリプトでSampleClassを作成します。

SampleClassの:の右に継承元のクラスを記述するので、この場合はSampleClassはMonoBehaviourクラスを継承して作られている事がわかります。

MonoBehaviourが親クラスでSampleClassが子クラスということになります。

SampleClassでStartやUpdateメソッドを記述した時、ゲームオブジェクトが登場した時にStart、ゲームオブジェクトが存在している時にUpdateがフレーム毎に呼ばれるのは、MonoBehaviourクラスでそれらが定義されているからです。

Unity のマニュアルは、Unityの使い方を学ぶ手助けとなります。Unityを使って 2D や 3D ゲームを作成したり、ゲーム以外のアプリを作成したりして多くの経験を積みましょう。

↑のページを見るとAwakeやStart、UpdateやさらにはOnTriggerEnterといったメソッドも定義されている事がわかります。

そのページの上の方に継承という部分でMonoBehaviourはBehaviourを継承して作られている事がわかります。

継承のBehaviourの部分をクリックすると、BehaviourはComponentを継承して作られ、ComponentはObjectを継承して作られている事がわかりました。

最終的にObjectクラスを継承して作ったクラスをさらに継承していってこのSampleClassが作られたというわけですね。

SampleClassでは独自のフィールドやメソッドを定義出来ますが、MonoBehaviourで定義されているフィールドやメソッドはそのまま使えるってわけですね。

継承を使わずにプログラミングは出来ないですが、実はすでに継承を使っていたんですね。(^^)/

すでに使っていると思えば敬遠する必要はなさそうですね。

Unityでインタフェース、抽象クラスを継承したサンプル作り

継承の説明ばかりではわからない事もあるので、実際にインタフェースと抽象クラスを作成し、それらを継承して新しいクラスを作成してみます。

インタフェース

インタフェースはクラスを抽象化していった最終形態で、インタフェースでは定数とpublicな抽象メソッドを宣言出来ます。

インタフェースは何のために作るかというと、最低限これらの機能を持たせるといった注文書やお店のメニューみたいなものです。

キャラクタークラスが持つコマンドをインタフェースとして作成してみます。

interfaceの後にIを付けてインタフェース名を記述します。

インタフェースは最初にIを付けるみたいです(インタフェースとわかりやすくする為)。

ICommandインタフェースではAttack、Defence、Itemという抽象メソッドを宣言しています。

インタフェースではメソッドは自動でpublicになります。

またメソッドの中身(実装)を記述する事は出来ず、{}を記述しないで;を書きます。

インタフェースは継承先のクラスで必ずこれらのメソッドを実装しなければいけないものを記述していますので、継承先でそれぞれのメソッドを実装(処理の記述)する必要があります。

またインタフェースはインスタンスを生成して使うクラスではない為、インスタンス化しようとするとエラーが発生します。

C#ではクラスの多重継承は許されていませんが、インタフェースの多重継承は可能です。

抽象クラス

抽象クラスは1つ以上の抽象メソッドを持つクラスでabstractを付けて宣言します。

この機能は必ず持たせ、実装するのは子クラスで、というような時に使用します。

例えば、Humanという抽象クラスを作り、その中には抽象メソッドとしてTalkを宣言したとします。
(抽象メソッドは子クラスで必ず実装しなければいけなくなります)。

Humanクラスを継承して作ったMasashiとMusashiは抽象クラスの抽象メソッドであるTalkメソッドを必ず実装しなければいけない為、Talkメソッドを記述します。

それぞれ別の処理を書いておけば、話す内容を別にする事が出来ます。

抽象クラスであるHumanクラスをobjectクラスを継承する形で作成してみます。

Humanクラスでは名前と性別のフィールドとそれらを初期化するコンストラクタ、フィールドのゲッターを用意します。

Humanクラスでは継承先でオーバーライド(上書き)するべきTalkメソッドをvirtualを付けて宣言しています。

Walkは抽象メソッドで、継承先で実装する必要があります。

継承先で継承元と同じメソッドを定義出来るのは(しなくてはいけない物も含む)は継承元でvirtualかabstractが付けられたメソッドに限ります。

抽象クラスもインタフェースと同じように抽象メソッドを含みますので、インスタンス化しようと思うとエラーが発生します。

インタフェース(ICommand)と抽象クラス(Human)を継承してCharaクラスを作成

インタフェースと抽象クラスの作成が出来たので、それらを継承してCharaクラスを作成します。

インタフェースは多重継承可能ですが、通常のクラスや抽象クラスの多重継承は出来ません。

今回の場合はインタフェースと抽象クラスを継承するので、:の後に,を使って継承するクラスを指定します。

Charaクラスのコンストラクタでは:の後に親クラスのコンストラクタを呼び出し引数の値を親クラスのフィールドにも設定しています。

抽象クラスであるHumanでTalkメソッドはvirtual、Walkはabstractで宣言されている為に、Charaクラスでoverrideを付けてメソッドをオーバーライドしています。

インタフェースで宣言された抽象メソッドや抽象クラスで宣言された抽象メソッドは継承先の子クラスで実装する必要があります。

子クラスで実装されなかった場合はそのクラスを継承するクラスで実装します。

抽象メソッドが実装されていない場合インスタンス化が出来ません。

Charaクラスをインスタンス化して実行する

Charaクラスはインスタンス化して使用するクラスなのでそれを使うクラスを作成し、インスタンス化してみます。

Charaクラスをインスタンス化するクラスをMakeCharacterクラスとし、Unityのゲームオブジェクトに取り付けて使用する為、MonoBehaviourクラスを継承して作成します。

↑のようにCharaクラスからmasashiとhanakoを作成してます。

masashiの場合はChara型の変数に参照を入れている為、自身や親クラスのメソッドを呼び出せています(アクセス修飾子がpublicな為)。

hanakoの場合はCharaの親クラスのHuman型の変数に参照を入れています。

その為Charaクラスで定義されているAttackメソッド等はキャストをしないとエラーになります(親クラスのメソッドにvirtualが付いて子クラスでoverrideしていれば出ない)。

親クラスの型の変数に代入する利点は同じ親クラスを持つ2つのクラスがあったとして、親クラスのメソッドを共通して使えたりする事です。

これに関しては後で実験してみます。

MakeCharacterスクリプトをMainCamera等に取り付けUnityを実行して確認してみてください。

メソッドのオーバーライドについて

親クラスの抽象メソッドを子クラス側でオーバーライドする際、親クラスのメソッドでvirtualかabstractのキーワードが付いている必要があります。

virtualは仮想メソッドのキーワードでこれが付いているメソッドは、子クラスで同じメソッドをoverrideキーワードを付けて上書きする必要があります。

この場合、子クラスを親クラスの参照変数に入れて親クラスのメソッドを呼び出した場合子クラスのメソッドが実行されます。

試しに親クラスのHumanにvirtualのSleepメソッド、子クラスのCharaにoverrideしたSleepメソッドを書きます。

↑のようにし、MakeCharacterクラスで、masashi、hanakoでそれぞれSleepを呼び出してみます。

↑を実行すると、ともに「CharaクラスのSleepメソッド」が表示されます。

メソッドをoverrideキーワードでオーバーライドすると自身のクラスのメソッドが呼ばれるようです。

hanakoの場合はHuman型の参照変数に入れているので、Humanクラスのメソッドを呼び出すようにしたい場合もあります。

そんな時はCharaクラスのSleepメソッドをoverrideではなくnewキーワードを付けて記述します。

先ほどのスクリプトを実行すると、

masashi.Sleepでは「CharaクラスのSleepメソッド」
hanako.Sleepでは「HumanクラスのSleepメソッド」

がコンソールに表示されます。

このようにすれば、Humanクラスを継承した子クラスをHuman型の参照変数に入れておけば、Humanクラスのメソッドを実行する事が出来ます。

newキーワードはポリモーフィズム(多態性)が使えなくなるので使わない方がいいみたいです・・・(^_^;)

virtualはoverrideして親クラスのSleepを使う時は、親クラスにキャストして使った方が良さそうですね。

参照サイト

C# と .NET での継承

ポリモーフィズム (C# プログラミング ガイド)

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます