基礎から学ぶ継承とポリモーフィズム
C# で理解するオブジェクト指向の核心
目次
1. はじめに
オブジェクト指向(OOP)の 4 本柱のうち、継承 (Inheritance) と ポリモーフィズム (Polymorphism) は「共通化」と「拡張性」を支える最重要キーワードです。この記事では、まだ List<T> やラムダ式に触れていない初学者でもイメージしやすいように、動物クラスの例を通じてステップバイステップで解説します。
2. 継承 ―「親の特性を受け継ぐ」
2-1 継承のイメージ
- 比喩:「犬は動物の一種」という自然な関係をそのままコードに落とし込みます。
 - 目的:
- コード再利用 – 共通コードは親クラスに一度だけ書く
 - 階層化 – 大枠→詳細へ思考を整理できる
 
 
2-2 C# の基本構文
class Animal   // 親(基底)クラス
{
    public string Name { get; set; }
    public void Eat()    // 全動物共通の振る舞い
    {
        Console.WriteLine($"{Name} は食事をする");
    }
}
class Dog : Animal  // 子(派生)クラス
{
    public void Bark()
    {
        Console.WriteLine($"{Name} はワン!と鳴く");
    }
}
- Dog : Animal の : が 「Animal を継承」 を表します。
 - Dog は Eat() を自動で持ち、独自メソッド Bark() を追加できます。
 
🛠 使い方(Console アプリ編)
- Visual Studio
- 「新しいプロジェクト」→ コンソール アプリ(.NET 8 以上推奨)を作成
 - Animal.cs, Dog.cs, Program.cs の 3 ファイル構成にする
 
 
// Program.cs
using System;
class Program
{
    static void Main()
    {
        var pochi = new Dog { Name = "ポチ" };
        pochi.Eat();   // Animal のメソッド
        pochi.Bark();  // Dog 独自メソッド
    }
}
2-3 継承の注意点
| 落とし穴 | 具体例 | 回避策 | 
|---|---|---|
| 深すぎる階層 | Animal → Mammal → Canine → Dog → … と 6 段以上 | 3〜4 段で再設計を検討 | 
| is-a が成り立たない継承 | Bird : Airplane など | 共通化は インターフェースで代替 | 
| 過剰な protected | 何でも派生先から触れる | private とプロパティで公開範囲を最小化 | 
3. ポリモーフィズム ―「同じ呼び出しが多彩に振る舞う」
3-1 ポリモーフィズムとは
- ギリシャ語で 「多形」。
 - 共通の型参照で実体に応じたメソッドが動く仕組み。
 
3-2 virtual / override による動的ポリモーフィズム
class Animal
{
    public string Name { get; set; }
    public virtual void Speak()   // 上書き可
    {
        Console.WriteLine($"{Name} は何か声を出す");
    }
}
class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} はワン!と鳴く");
    }
}
class Cat : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} はニャーと鳴く");
    }
}
- Speak() 呼び出し一つでDog と Cat が異なる鳴き声を出す。
 - 拡張に強い:Bird を追加しても foreach 側は変更不要。
 
🛠 使い方(Console アプリ編)
- 先ほどと同様に Console プロジェクトを用意。
 - Animal.cs, Dog.cs, Cat.cs, Program.cs を配置:
 
// Program.cs
using System;
class Program
{
    static void Main()
    {
        Animal[] zoo =
        {
            new Dog { Name = "ポチ" },
            new Cat { Name = "タマ" }
        };
        foreach (var a in zoo)
            a.Speak();   // 実体に応じた出力が得られる
    }
}
実行 →
ポチ はワン!と鳴く
タマ はニャーと鳴く
3-3 インターフェースによるポリモーフィズム
interface IFlyable
{
    void Fly();
}
class Bird : Animal, IFlyable
{
    public override void Speak() => Console.WriteLine($"{Name} はチュンと鳴く");
    public void Fly()            => Console.WriteLine($"{Name} が飛ぶ");
}
class Plane : IFlyable
{
    public void Fly() => Console.WriteLine("飛行機が離陸する");
}
- 共通点は“飛べる”ことだけ。
 - 2 つを IFlyable 配列に入れて Fly() を呼び出せば、実体ごとに適した挙動が発動。
 
🛠 使い方
using System;
class Program
{
    static void Main()
    {
        IFlyable[] flyers =
        {
            new Bird { Name = "ピー" },
            new Plane()
        };
        foreach (var f in flyers)
            f.Fly();
    }
}
4. 継承 × ポリモーフィズムを Unity で試す
シーン:AnimalController スクリプトを空の GameObject に付けて実行
4-1 スクリプト
// Animal.cs (Unity でも同じクラスを利用)
public class Animal
{
    public string Name { get; set; }
    public virtual void Speak() => Debug.Log($"{Name} は何か声を出す");
}
public class Dog : Animal
{
    public override void Speak() => Debug.Log($"{Name} はワン!");
}
// Bird なども同様...
// AnimalController.cs
using UnityEngine;
public class AnimalController : MonoBehaviour
{
    private void Start()
    {
        Animal[] animals =
        {
            new Dog { Name = "ポチ" },
            new Cat { Name = "タマ" },
            new Bird { Name = "ピー" }
        };
        foreach (var a in animals)
            a.Speak();
    }
}
🛠 使い方(Unity 6)
- Unity Hub で 3D Core テンプレートのプロジェクトを新規作成。
 - Scripts フォルダーを作り、上記 .cs ファイルを保存。
 - Hierarchy で Create → Empty。オブジェクト名は GameManager など。
 - AnimalController.cs を GameManager にドラッグ&ドロップ。
 - Play ボタン → Console に 3 行の鳴き声が表示されれば成功。
 
- コンソールに「ポチ はワン!」「タマ はニャー」「ピー はチュン」が順に表示。
 - Unity のスクリプトでも同じ文法で動くことを確認できます。
 
5. 練習課題
- Fish クラスを Animal から継承し、Speak() を「はブクブク…」にする。
 - IFlyable を実装しているクラスをシーン上でランダムに飛ばすミニゲームを作る。
 - 「継承よりコンポジション(部品化)が適切」と思われるケースを 3 つ挙げる。
 
1. Fish クラスを実装し、Speak() を「〇〇はブクブク…」にする
class Fish : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} はブクブク…");
    }
}
確認コード
var nemo = new Fish { Name = "ニモ" };
nemo.Speak();   // => ニモ はブクブク…
2. IFlyable を実装しているクラスをランダムに飛ばすミニゲーム(Unity)
- インターフェースと実装クラス
 
public interface IFlyable
{
    void Fly(Vector3 direction, float speed);
}
public class Bird : Animal, IFlyable
{
    public override void Speak() => Debug.Log($"{Name} はチュン");
    public void Fly(Vector3 direction, float speed)
        => transform.Translate(direction.normalized * speed * Time.deltaTime);
}
public class Plane : MonoBehaviour, IFlyable
{
    public void Fly(Vector3 direction, float speed)
        => transform.Translate(direction.normalized * speed * Time.deltaTime);
}
- コントローラースクリプト空の GameObject に追加してください。
 
using UnityEngine;
public class FlyManager : MonoBehaviour
{
    [SerializeField] private MonoBehaviour[] flyables; // Bird, Plane など
    private void Start()
    {
        InvokeRepeating(nameof(RandomFly), 1f, 2f); // 2 秒ごとに呼び出し
    }
    private void RandomFly()
    {
        if (flyables.Length == 0) return;
        // ランダムに 1 体取得
        var f = (IFlyable)flyables[Random.Range(0, flyables.Length)];
        // ランダム方向と速度
        Vector3 dir  = Random.onUnitSphere;  // 3D 空間なら
        float   speed = Random.Range(1f, 5f);
        f.Fly(dir, speed);
    }
}
ポイント
- 配列の型を MonoBehaviour にしているのは Unity エディタでドラッグ&ドロップ登録しやすくするため。
 - 呼び出し側 (FlyManager) は IFlyable しか意識しておらず、Bird を追加してもコード変更不要です。
 
3. 継承よりコンポジションが適切なケース例
| ケース | 継承では問題になる点 | コンポジションでの設計イメージ | 
|---|---|---|
| ① 武器を装備できるキャラクター | Swordman : Character, Archer : Characterなどを増やすとクラス爆発 | Character が Weapon を「持つ」。IWeapon インターフェースを介し、動的に交換可能 | 
| ② UI のボタン装飾 | RedButton : Button, GreenButton : Button, … 色・大きさごとに派生が増える | Button に Style オブジェクトを注入し、色・枠線・サイズをパラメータ化 | 
| ③ 複数の機能を組み合わせる家電 | SmartWasherDryerMicrowave : Applianceのような不自然な is-a 関係 | 小機能(洗う・乾かす・温める)を個別クラス化し、Appliance が必要な機能を「持つ」 | 
判断の目安
- is-a(~は~である)があいまい / 無理やり
 - 機能の組み合わせパターンが多い
 - ランタイムに振る舞いを差し替えたい
 
その場合は コンポジション(部品化)+インターフェース を優先しましょう。
まとめ
サンプル回答を動かしながら次を試すと理解が深まります。
- Fish を Animal[] に混ぜて Speak() ポリモーフィズムを再確認
 - IFlyable 実装クラスを増やし、FlyManager はノータッチで拡張できるか検証
 - 自身のプロジェクトで「継承 vs コンポジション」を意識し、クラス設計を見直す
 
小さなサンプルでも “変更に強い” を体験することで、オブジェクト指向の本当のメリットが体に染み込みます。
6. まとめ
| キーワード | 一言まとめ | 
|---|---|
| 継承 (Inheritance) | 「親の共通機能を子どもが受け継ぐ」 | 
| ポリモーフィズム (Polymorphism) | 「親型参照で子どもを自在に扱う」 | 
- 継承は コード再利用と階層化、ポリモーフィズムは 拡張性と柔軟性 をもたらします。
 - 理解のコツは、*「Dog は Animal である」*と *「Animal 配列の中身は何になるかわからない」*の 2 点を意識すること。
 - まずはサンプルを コピペ→実行→改造 し、振る舞いが切り替わる瞬間を体験してみましょう。
 
次のステップ
- abstract クラスと sealed クラスの使いどころ
 - インターフェース多重実装と DI (依存性注入) の基本
 - 継承を避けたいケースの「コンポジション vs 継承」比較
 
OOP の旅はまだ始まったばかりですが、継承とポリモーフィズムを掴めれば、柔軟に拡張できる設計への第一歩を踏み出せます。ぜひ自分のプロジェクトで試してみてください。
訪問数 25 回, 今日の訪問数 1回






ディスカッション
コメント一覧
まだ、コメントがありません