p.232(多態性の説明の前に…基本クラスの参照変数について)
・派生クラスのインスタンスは、基本クラスの参照変数で扱うことができる
・例えば「ホイミスライムのホイミンはスライムとして扱えること」と考えると良い
例:
class Slime {…}
class HoimiSlime : Slime {…}
:
HoimiSlime hoimin = new HoimiSlime();
Slime slime1go = hoimin; //継承しているので型は違うが同じオブジェクトを参照できる
・このことを利用して、複数の派生クラスのオブジェクトを、基本クラスを型としてまとめて扱える
・例えば「モンスターから派生した各種の魔物はすべてモンスターで扱える」
・具体的には、基本クラスを型とする配列には、派生クラスのインスタンスを格納できる
例:
Slime[] slimes = {hoimin, behoimin, sulalin}; //派生クラスのオブジェクトを基本クラスの配列へ
p.232 多態性
・派生クラスのオブジェクトを基本クラスの型であつかうと、名前の隠ぺいを行っている場合、基本クラスで扱われる
例:
class Slime {public void name(){ Console.WriteLine("スライムです");}
class HoimiSlime : Slime {new public void name(){ Console.WriteLine("ホイミスライムです");;}
:
Slime hoimin = new HoimiSlime();
hoimin.name(); //「スライムです」と表示
・しかし、元は派生オブジェクトなのだから、派生クラス側のメソッドが動作するのが適切
・これが多態性で、オーバーライドメソッドにおいて可能になる
例:
class Slime {public virtual void name(){ Console.WriteLine("スライムです");}
class HoimiSlime : Slime {public override void name(){ Console.WriteLine("ホイミスライムです");;}
:
Slime hoimin = new HoimiSlime();
hoimin.name(); //「ホイミスライムです」と表示
・多態性を用いると、複数の派生クラスのオブジェクトを、基本クラスを型としてまとめて扱っても、元の定義が活きる。
・よって通常、メソッドにおいては名前の隠ぺいではなくオーバーライドを用いる。
・多態性により、基本クラスのメソッドに代わって派生クラスのオーバーライドメソッドのどれを呼ぶかは実行時に決まるので、動的メソッドディスパッチという。
p.233 override02.cs
//p.233 override02.cs
using System;
class Mammal { //基本クラス(哺乳類)
protected const int LegNo = 4; //脚の数は4本
protected string Koe; //鳴き声
public virtual string Nakigoe() { //声を返す仮想メソッド
Koe = "..."; //哺乳類の鳴き声は不定なので「...」にしておく
return Koe;
}
public int Leg() { //脚の数を返す通常メソッド
return LegNo;
}
}
class Cat : Mammal { //派生クラス(猫)
public override string Nakigoe() { //声を返すオーバーライドメソッド
Koe = "ニャー、ニャー"; //猫の鳴き声にする
return Koe;
}
}
class Dog : Mammal { //派生クラス(猫)
public override string Nakigoe() { //声を返すオーバーライドメソッド
Koe = "ワン、ワン"; //犬の鳴き声にする
return Koe;
}
}
class override02 {
public static void Main() {
Mammal m; //哺乳類クラスとその派生クラスを扱うための参照変数のみを定義
Cat cat = new Cat(); //派生クラス(猫)のインスタンスを生成
Dog dog = new Dog(); //派生クラス(犬)のインスタンスを生成
m = cat; //派生クラス(猫)のインスタンスを基本クラス(哺乳類)で扱う
Console.WriteLine("猫の脚は{0}本で鳴き声は「{1}」です",
m.Leg(), m.Nakigoe()); //通常メソッドとオーバーライドメソッド(猫)を呼ぶ
m = dog; //派生クラス(犬)のインスタンスを基本クラス(哺乳類)で扱う
Console.WriteLine("犬の脚は{0}本で鳴き声は「{1}」です",
m.Leg(), m.Nakigoe()); //通常メソッドとオーバーライドメソッド(犬)を呼ぶ
}
}
アレンジ演習:p.233 override02.cs
・基本クラス型の配列に、派生クラス型の2インスタンスを格納しよう
・配列の全要素について繰り返す中で、オーバーライドメソッドを呼んで多態性を確認しよう
作成例
//アレンジ演習:p.233 override02.cs
using System;
class Mammal { //基本クラス(哺乳類)
protected const int LegNo = 4; //脚の数は4本
protected string Koe; //鳴き声
public virtual string Nakigoe() { //声を返す仮想メソッド
Koe = "..."; //哺乳類の鳴き声は不定なので「...」にしておく
return Koe;
}
public int Leg() { //脚の数を返す通常メソッド
return LegNo;
}
}
class Cat : Mammal { //派生クラス(猫)
public override string Nakigoe() { //声を返すオーバーライドメソッド
Koe = "ニャー、ニャー"; //猫の鳴き声にする
return Koe;
}
}
class Dog : Mammal { //派生クラス(犬)
public override string Nakigoe() { //声を返すオーバーライドメソッド
Koe = "ワン、ワン"; //犬の鳴き声にする
return Koe;
}
}
class override02 {
public static void Main() {
Cat cat = new Cat(); //派生クラス(猫)のインスタンスを生成
Dog dog = new Dog(); //派生クラス(犬)のインスタンスを生成
Mammal[] m = {cat, dog}; //哺乳類クラスとその派生クラスを扱うための配列に格納
foreach (var w in m) { //哺乳類の配列の全要素について繰返す
Console.WriteLine("脚は{0}本で鳴き声は「{1}」です",
w.Leg(), w.Nakigoe()); //通常メソッドとオーバーライドメソッドを呼ぶ
}
}
}
p.235 プロパティのオーバーライド
・メソッドと同様で、多態性を実現できる
・基本クラスのプロパティを仮想プロパティにし、派生クラスのプロパティをオーバーライドプロパティにする
p.236 override03.cs
//p.236 override03.cs
using System;
class Mammal { //基本クラス(哺乳類)
protected const int LegNo = 4; //脚の数は4本
public virtual string Nakigoe { //声を返す仮想プロパティ
get { return "..."; } //setがないプロパティ
}
public int Leg() { //脚の数を返す通常メソッド
return LegNo;
}
}
class Cat : Mammal { //派生クラス(猫)
public override string Nakigoe { //声を返すオーバーライドプロパティ
get { return "ニャー、ニャー"; }
}
}
class Dog : Mammal { //派生クラス(犬)
public override string Nakigoe { //声を返すオーバーライドプロパティ
get { return "ワン、ワン"; }
}
}
class override03 {
public static void Main() {
Mammal m; //哺乳類クラスとその派生クラスを扱うための参照変数を定義
Cat cat = new Cat(); //派生クラス(猫)のインスタンスを生成
Dog dog = new Dog(); //派生クラス(犬)のインスタンスを生成
m = cat; //派生クラス(猫)のインスタンスを基本クラス(哺乳類)で扱う
Console.WriteLine("猫の脚の数は{0}本で、鳴き声は「{1}」です",
m.Leg(), m.Nakigoe); //通常メソッドとオーバーライドプロパティ(猫)を呼ぶ
m = dog; //派生クラス(犬)のインスタンスを基本クラス(哺乳類)で扱う
Console.WriteLine("犬の脚の数は{0}本で、鳴き声は「{1}」です",
m.Leg(), m.Nakigoe); //通常メソッドとオーバーライドプロパティ(犬)を呼ぶ
}
}
<自由参加課題> アレンジ演習:p.236 override03.cs
・基本クラス型の配列に、派生クラス型の2インスタンスを格納しよう
・配列の全要素について繰り返す中で、オーバーライドプロパティを呼んで多態性を確認しよう
p.237(オーバーライドインデクサ)
・メソッドと同様で、多態性を実現できる
・基本クラスのインデクサを仮想インデクサにし、派生クラスのインデクサをオーバーライドインデクサにする
作成例
//p.237 override04.cs
using System;
class Mammal { //基本クラス(哺乳類)
protected const int LegNo = 4; //脚の数は4本
protected string Tail, Gei, Food, Koe; //特徴を示す文字列変数
public virtual string this[string index] { //声を返す仮想インデクサ
get { return "..."; }
}
public int Leg() { //脚の数を返す通常メソッド
return LegNo;
}
}
class Cat : Mammal { //派生クラス(猫)
public override string this[string index] { //オーバーライドインデクサ
get {
switch (index) { //インデックスの値により返す情報を選択する
case "尾":
Tail = "1本";
return Tail;
case "芸":
Gei = "できない";
return Gei;
case "鳴き声":
Koe = "ニャー、ニャー";
return Koe;
case "食べ物":
Food = "キャットフード";
return Food;
default:
return "";
}
}
}
}
class Dog : Mammal { //派生クラス(犬)
public override string this[string index] { //オーバーライドインデクサ
get {
switch (index) { //インデックスの値により返す情報を選択する
case "尾":
Tail = "1本";
return Tail;
case "芸":
Gei = "できる";
return Gei;
case "鳴き声":
Koe = "ワン、ワン";
return Koe;
case "食べ物":
Food = "ドッグフード";
return Food;
default:
return "";
}
}
}
}
class override04 {
public static void Main() {
Mammal m;
Cat cat = new Cat();
Dog dog = new Dog();
m = cat;
//文字列をインデックスとしてオーバライドインデクサ(猫)を呼ぶ
Console.WriteLine("猫の脚は{0}本です。尾は{1}です。芸は{2}。食べ物は{3}。",
m.Leg(), m["尾"], m["芸"], m["食べ物"]);
m = dog;
//文字列をインデックスとしてオーバライドインデクサ(犬)を呼ぶ
Console.WriteLine("犬の脚は{0}本です。尾は{1}です。芸は{2}。食べ物は{3}。",
m.Leg(), m["尾"], m["芸"], m["食べ物"]);
}
}
p.248 クラスの多層階層
・派生クラスを基本クラスとしてさらに派生することが可能(親子孫のイメージ)
・基本クラスのメンバは派生の派生クラスにも引き継がれる
・派生の派生クラスのオブジェクトを基本クラスの型で扱うことができる
・派生クラスで独自に記述したメソッドを派生の派生クラスでオーバライドするには、仮想メソッドにする
・派生クラスのオーバライドメソッドを派生の派生クラスでオーバライドするには、オーバライドメソッドで良い
p.240 inheritance05.cs
//p.240 inheritance05.cs
using System;
class MyBase { //基本クラス
protected int x = 10; //継承可能なデータメンバ
public virtual void show() { //オーバライド可能な仮想メソッド
Console.WriteLine("x = {0}", x);
}
}
class Derived1 : MyBase { //派生クラス
//ここに「protected int x = 10;」があるとみなされる
protected int y = 20; //継承可能なデータメンバ
//ここに「public virtual void show() {…}」があるとみなされる
}
class Derived2 : Derived1 { //派生の派生クラス
//ここに「protected int x = 10;」があるとみなされる
//ここに「protected int y = 20;」があるとみなされる
int z = 30;
public override void show() { //派生クラスが継承したメソッドのオーバライド
Console.WriteLine("z = {0}", z);
}
}
class inheritance05 {
public static void Main() {
MyBase mb; //派生クラスと派生の派生クラスを扱うための参照変数を定義
Derived1 d1 = new Derived1(); //派生クラスのインスタンスを生成
Derived2 d2 = new Derived2(); //派生の派生クラスのインスタンスを生成
mb = d1; //派生クラスのインスタンスを基本クラスで扱う
mb.show(); //多態性により派生クラスのメソッドが動作
mb = d2; //派生の派生クラスのインスタンスを基本クラスで扱う
mb.show(); //多態性により派生の派生クラスのメソッドが動作
}
}
p.243 クラスの継承とコンストラクタ(引数がない場合)
・どのクラスにおいても、コンストラクタを全く記述しないと、引数のない&中身のないコンストラクタが自動的に用意される
・基本クラスのオブジェクトを引数なしのnewで生成すると、引数のないコンストラクタが呼ばれる
・派生クラスのオブジェクトを引数なしのnewで生成すると:
① 基本クラスの引数のないコンストラクタが呼ばれる
② 派生クラスの引数のないコンストラクタが呼ばれる
・こうすることで、基本クラスから継承したデータメンバの初期化などの処理も正しく行われる
・また、その派生クラスには不要でも基本クラスの機能を引き継ぐ上で必要な準備処理が正しく行われる
・よって、派生の派生クラスのオブジェクトを引数なしのnewで生成すると:
① 基本クラスの引数のないコンストラクタが呼ばれる
② 派生クラスの引数のないコンストラクタが呼ばれる
③ 派生の派生クラスの引数のないコンストラクタが呼ばれる
p.243 inheritance06.cs
//p.243 inheritance06.cs
using System;
class MyBase { //基本クラス
protected int x; //継承可能なデータメンバ
public MyBase() { //①引数の無いコンストラクタ
Console.WriteLine("ここはMyBase");
x = 10; //データメンバの初期化
}
}
class Derived1 : MyBase { //派生クラス
//ここに「protected int x;」があるとみなされる
public Derived1() { //②引数の無いコンストラクタ
//この直前に基本クラスの①引数の無いコンストラクタが実行される
Console.WriteLine("ここはDerived1");
x = 20; //データメンバの初期化
}
}
class Derived2 : Derived1 { //派生の派生クラス
//ここに「protected int x;」があるとみなされる
public Derived2() { //③引数の無いコンストラクタ
//この直前に基本クラスの①引数の無いコンストラクタが実行される
//この直前に派生クラスの②引数の無いコンストラクタが実行される
Console.WriteLine("ここはDerived2");
x = 30; //データメンバの初期化
}
public void show() { //派生の派生クラスの独自のメソッド
Console.WriteLine("x = {0}", x);
}
}
class inheritance06 {
public static void Main() {
Derived2 d2 = new Derived2(); //派生の派生クラスのインスタンスを生成(①②③の実行)
d2.show(); //③が最後に動くので30になっている
}
}
p.245 基本クラスのコンストラクタが引数をとる場合
・例えば、派生の派生クラスのコンストラクタの引数を「int i」とし、派生クラスのコンストラクタの引数も「int i」とするとエラーになる
・これは、派生クラスのコンストラクタの引数を与える手段がないから
・同様に、基本クラスのコンストラクタの引数も「int i」とするとエラーになる
・これは、基本クラスのコンストラクタの引数を与える手段がないから
・これを解決するのが、コンストラクタにおけるbaseキーワードによる引数渡しの指定
・書式例: public クラス名(引数リスト) : base(引数,…){…}
・baseキーワードに指定する引数はコンストラクタの引数を用いると良い
・例: public Derived2(int i) : base(i) {…} //派生クラスのコンストラクタに引数iの値を渡す
・例: public Derived1(int i) : base(i) {…} //基本クラスのコンストラクタに引数iの値を渡す
提出:アレンジ演習:p.243 inheritance06.cs
・3つのクラスのコンストラクタの引数をすべて「int i」にしたい
・xの初期値をiで与えるとする
・派生クラスのコンストラクタと、派生の派生クラスのコンストラクタにbaseキーワードを追加しよう
・Mainで派生の派生クラスのコンストラクタに引数で90を与えてみよう