基礎から学ぶC#入門 90日コース | 継承・抽象化・インターフェース - Day 42:virtual・override

C# 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 42 のゴールと全体像

Day 42 のテーマは virtualoverride と多態性(ポリモーフィズム)です。 継承を一歩進めて、

  • 仮想メソッド(virtual
  • オーバーライド(override
  • 多態性(ポリモーフィズム)

という、オブジェクト指向の「おいしいところ」に入っていきます。

ざっくり言うと、

「同じメソッド名なのに、クラスごとに違う動きをさせる仕組み」

を理解していく回だと思って読んでいただけると、 スッと入ってきやすいです。

仮想メソッド(virtual)とは何か

「子クラスで上書きしていいですよ」という宣言

まずは 仮想メソッド(virtual メソッド) からです。

virtual は、

「このメソッドは、派生クラスで上書き(オーバーライド)してもいいですよ」

という意味のキーワードです。

例として、動物クラスを考えてみます。

using System;

// 基底クラス:動物
class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    // 仮想メソッド:鳴く
    public virtual void Speak()
    {
        Console.WriteLine($"{Name} は何か鳴きます。");
    }
}
C#

ここでのポイントは、

  • public virtual void Speak() → 「この Speak は、子クラスで上書きしてもよいメソッドですよ」という宣言

です。

オーバーライド(override)とは何か

「親のメソッドを、自分流に書き換える」

次に オーバーライド(override) です。

override は、

「基底クラスの仮想メソッドを、派生クラスで自分流に書き換える」

ためのキーワードです。

先ほどの Animal を継承して、 犬と猫のクラスを作ってみます。

// 派生クラス:犬
class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    // Speak を犬用にオーバーライド
    public override void Speak()
    {
        Console.WriteLine($"{Name} は『ワン!』と鳴きます。");
    }
}

// 派生クラス:猫
class Cat : Animal
{
    public Cat(string name) : base(name)
    {
    }

    // Speak を猫用にオーバーライド
    public override void Speak()
    {
        Console.WriteLine($"{Name} は『ニャー!』と鳴きます。");
    }
}
C#

ここでのポイントは、

  • public override void Speak() → 基底クラス Animalvirtual Speak を「上書き」している
  • メソッド名・戻り値・引数の形は、基底クラスと同じでなければならない

というところです。

多態性(ポリモーフィズム)を体感する

「同じ型として扱っているのに、動きは違う」

virtualoverride の真価は、 「多態性(ポリモーフィズム)」を使ったときに現れます。

多態性とは、

「同じ型として扱っているのに、実際の中身によって動きが変わること」

です。

先ほどの AnimalDogCat を使って、 こんなコードを書いてみます。

class Program
{
    static void Main(string[] args)
    {
        // Animal 型の変数に、Dog と Cat のインスタンスを入れる
        Animal a1 = new Dog("ポチ");
        Animal a2 = new Cat("タマ");

        // 同じように Speak を呼んでいるのに、実際の動きは違う
        a1.Speak(); // ポチ は『ワン!』と鳴きます。
        a2.Speak(); // タマ は『ニャー!』と鳴きます。

        Console.ReadLine();
    }
}
C#

ここが、多態性の一番おもしろいところです。

  • 変数の型はどちらも Animal
  • 呼び出しているメソッドはどちらも Speak()
  • なのに、実際の動きは「犬の鳴き方」「猫の鳴き方」に変わる

これは、

  • 基底クラスで virtual を宣言し
  • 派生クラスで override している

からこそ実現できる動きです。

重要ポイント:

  • 多態性は「共通のインターフェース(メソッド名)で扱いつつ、 実際のクラスごとに違う振る舞いをさせる」ための仕組みです。

なぜ多態性がうれしいのか

1. 共通の処理を書きやすくなる

例えば、動物がたくさんいる動物園を考えてみます。

class Zoo
{
    private readonly List<Animal> animals = new List<Animal>();

    public void AddAnimal(Animal animal)
    {
        animals.Add(animal);
    }

    public void MakeAllAnimalsSpeak()
    {
        Console.WriteLine("動物園の動物たちが一斉に鳴きます。");
        foreach (var animal in animals)
        {
            animal.Speak(); // 型は Animal だが、実際のクラスごとに違う鳴き方をする
        }
    }
}
C#

使う側はこうです。

class Program
{
    static void Main(string[] args)
    {
        Zoo zoo = new Zoo();

        zoo.AddAnimal(new Dog("ポチ"));
        zoo.AddAnimal(new Cat("タマ"));
        zoo.AddAnimal(new Dog("シロ"));

        zoo.MakeAllAnimalsSpeak();

        Console.ReadLine();
    }
}
C#

ここでは、

  • Zoo は「Animal のリスト」だけを意識している
  • 実際にどんな動物が入っているかは気にしない
  • Speak() を呼べば、それぞれの動物が自分らしく鳴いてくれる

という構造になっています。

これが、多態性の力です。

2. 新しいクラスを追加しても、既存コードをほとんど変えなくてよい

例えば、鳥クラス Bird を追加したくなったとします。

class Bird : Animal
{
    public Bird(string name) : base(name)
    {
    }

    public override void Speak()
    {
        Console.WriteLine($"{Name} は『チュンチュン』と鳴きます。");
    }
}
C#

このとき、

  • Zoo クラスは一切変更しなくてよい
  • MakeAllAnimalsSpeak() はそのまま動く
  • ただ zoo.AddAnimal(new Bird("ピーちゃん")); と追加するだけで、 鳥も一緒に鳴いてくれるようになります。

重要ポイント:

  • 多態性を使うと、「新しい種類」を追加するときに、 既存の処理をほとんど変えずに済みます。
  • これは、アプリが大きくなったときに、 保守性・拡張性を大きく高めてくれる考え方です。

virtual・override の基本テンプレート

最後に、virtualoverride・多態性の基本形をテンプレートとしてまとめます。

基底クラス

class Base
{
    public string Name { get; set; }

    public Base(string name)
    {
        Name = name;
    }

    // 仮想メソッド
    public virtual void DoWork()
    {
        Console.WriteLine($"{Name} が基本的な仕事をします。");
    }
}
C#

派生クラスA

class DerivedA : Base
{
    public DerivedA(string name) : base(name)
    {
    }

    // オーバーライド:A 用の仕事に書き換える
    public override void DoWork()
    {
        Console.WriteLine($"{Name} が A としての仕事をします。");
    }
}
C#

派生クラスB

class DerivedB : Base
{
    public DerivedB(string name) : base(name)
    {
    }

    // オーバーライド:B 用の仕事に書き換える
    public override void DoWork()
    {
        Console.WriteLine($"{Name} が B としての仕事をします。");
    }
}
C#

多態性を使う側

class Program
{
    static void Main(string[] args)
    {
        Base b1 = new DerivedA("オブジェクトA");
        Base b2 = new DerivedB("オブジェクトB");

        // 型は Base だが、実際のクラスごとに違う動きをする
        b1.DoWork(); // A としての仕事
        b2.DoWork(); // B としての仕事

        Console.ReadLine();
    }
}
C#

このテンプレートをベースに、

  • 「共通のメソッド名」を基底クラスに
  • 「クラスごとの違う動き」を派生クラスに

という形で、自分なりの例を作ってみると、 virtualoverride・多態性の感覚がぐっと身近になります。

Day 42 のまとめ

Day 42 では、

  • 仮想メソッド(virtual → 「子クラスで上書きしてよいメソッド」の宣言
  • オーバーライド(override → 「親の仮想メソッドを、自分流に書き換える」仕組み
  • 多態性(ポリモーフィズム) → 「同じ型・同じメソッド名なのに、実際のクラスごとに違う動きをする」性質

を、動物の例やテンプレートを通して学びました。

多態性は、オブジェクト指向の中でも特に強力で、

「新しい種類を追加しても、既存のコードをほとんど変えなくてよい」

という、大きなメリットをもたらしてくれます。

ぜひ、

  • 自分で基底クラスを1つ作り
  • 派生クラスを2〜3個作って
  • virtualoverride を使って「同じメソッド名で違う動き」を試してみる

という小さな練習を通して、 多態性を 「教科書の言葉」ではなく、 自分のコードの中で生きている感覚として、少しずつ育てていってください。

タイトルとURLをコピーしました