クラスの拡張(後編)

4日前ほどですかね。クラスの拡張(前編)を書いたので(すっかり忘れてました汗)今回は後編を書いていこうと思います。

前回は派生クラスの書き方についてお話ししましたね。なので今回は派生クラスの使い方、便利な点などを紹介したいと思います。

①派生クラスから基本クラスで宣言したイベントを扱うことのできる機能が備わっている。

②派生クラスは基本クラスのオブジェクトとして利用可能

前回のコードを出しておきましょう。

class Cat
{
    //猫の体重を表すフィールド
    double weight;
    //猫の身長を表すフィールド
    double height;
    public double Weight
    {
        get { return weight; }
        set
        {
            if (value > 0)
                weight = value;
        }
    }
    public double Height
    {
        get { return height; }
        set
        {
            if (value > 0)
                height = value;
        }
    }
    //鳴き声を定義
    public string mew()
    {
        return ("にゃ~");
    }
    //走ると100グラム体重が減る
    public void run()
    {
        weight -= 0.1;
    }
    //食べると体重が100グラム増える
    public void eat()
    {
        weight += 0.1;
        height += 0.01;
    }
}

↑これが基本クラス。少し手直ししてあります。

class MyCat : Cat
{
    //新しくswimming()メソッドを作成
    public void swimming()
    {
        Weight -= 0.5;
    }
    //基本クラスと同じ名前でも大丈夫
    public string mew()
    {
        return ("にゃぉーん");
    }
}

↑が派生クラスです。

話が前後してしまって申し訳ないのですが、もう少しコードを直していきましょう。

今、派生クラスを見てみると不思議な書き方をしていることにお気づきでしょうか?

そうですね。派生クラスは基底クラスで用意されたフィールドを利用するためにアクセサを利用していますね。

悪くはありませんが、回りくどく、利用しづらそうです。

なぜ、このようになってしまったのでしょう?

それは、基本クラスのフィールドの修飾子がprivateになっているため、派生関係があるにも関わらず、アクセスできないのです。

これを解決する術はないのでしょうか。

いえ、もちろんあります。

修飾子にprotectedと書くだけです。

これをすると、「派生関係があるもの以外」アクセスできなくなります。

なので、コードを書き直してみてください。

 

 

 これで、準備完了です。

またまた、前後しますが、②からやっていきます。

②の「派生クラスは基本クラスのオブジェクトとして利用可能」

 は下のように書きます。

Cat Nyanko = new MyCat();

↓のほうがわかりやすいかもしれません。

Cat Nyanko;
Nyanko = new MyCat();

使うシチュエーションが微妙な感じですが、 

これを応用すると、配列で一気に宣言することが可能になります。

Cat[] cat = new Cat[3];
cats[0] = new Cat();
cats[1] = new Cat();
cats[2] = new MyCat();

 ①は

for (i = 0; i < cats.Length; i++)
{
    cats[i].run();
}

としてみるとわかりやすいかもしれません。

しかし、この場合なら問題ありませんが、 

for (i = 0; i < cats.Length; i++)
{
    Console.Write(cats[i].mew());
}

としてみた途端問題が生じます。

MyCat Tama = new MyCat();
Console.Write(Tama.mew());

で実行すると、「にゃぉーん」と返ってくるはずで、そうなければならないはずなのに、基本クラスで定義した、「にゃー」と表示されます。

なぜ、そうなるかというと、そもそも、Catクラス(基本クラス)の配列なので、Catクラスのメソッドを呼び出しているのです。

これを解決するには、同じ名前で再定義されるものとするものにそれぞれvirtual、overrideをつけなければなりません。

そうすることで初めて、派生クラスのメンバが機能します。

↓基本クラス

virtual public string mew()
{
    return ("にゃ~");
}

↓派生クラス

override public string mew()
{
    return ("にゃぉーん");
}

 

中途半端感が残らなくもないですが、これにて解散

ではでは~