p.232 多態性
・クラスの継承は基本クラスを代表とする派生クラスのグルーピングにもなる
・例: Slimeクラスを継承するHoimiSlimeクラス、MetalSlimeクラスは「スライムの一族」
・よって、派生クラスのオブジェクトは、基本クラスを型とする変数に代入できる
・例: HoimiSlimeクラスのホイミンは、Slimeクラスのホイミンとしても扱える(メンバを引き継いでいるので)
・このとき、メソッドにおいて名前の隠蔽が起こっていると、基本クラスを型とする変数でメソッドを呼ぶと、基本クラスのメソッドが実行される
・しかし、メソッドにおいてオーバーライドが起こっていると、基本クラスを型とする変数でメソッドを呼んでも、
派生クラスのオーバーライドメソッドが実行される
・このことを多態性(ポリモフィズム)という
・例: Slimeクラスの仮想メソッドはHPのみ表示、継承するHoimiSlimeクラスのオーバライドメソッドはHPとMPを表示する場合:
HoimiSlimeクラスのホイミンは、Slimeクラスのホイミンとして扱っても、メソッドを呼ぶとオーバライドメソッドが動作してHPとMPを表示
・この「どのメソッドが実行されるかが実行時に決まる仕組み」を動的メソッドディスパッチャという
・例:
class Slime { //基本クラス
int hp; //基本クラスのインスタンス変数メンバ
public virtual void show() { //仮想メソッド
Console.Write(hp);
}
}
class HoimiSlime : Slime { //派生クラス
// ここに「int hp; //基本クラスのインスタンス変数メンバ」があるとみなされる
int mp;
// ここに「public virtual void show() {…} //基本クラスの仮想メソッド」があるがオーバーライドされる
public override void show() { //オーバーライドメソッド
Console.Write(hp + ", " + mp);
}
}
HoimiSlime hoimin = new HoimiSlime(); //ホイミンをホイミスライムとして生成
Slime slime1 = hoimin; //ホイミンをスライムクラスのスライム1号として扱う
slime1.show(); //スライム1号はSlimeだが、多態性により中身がホイミンと分かりオーバライドメソッドが呼ばれる
アレンジ演習:p.233 override02.cs
・名前の隠ぺいでは(オーバーライドは異なり)多態性が起こらないことを確認しよう ・Dogクラスに名前を隠ぺいするLeg()メソッドを追加し「return 0;」としてみよう
作成例
//アレンジ演習:p.233 override02.cs
using System;
class Mammal { //基本クラス「哺乳類」
protected const int LegNo = 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;
}
new public int Leg() { //【以下追加】足の数を返す通常メソッドの名前の隠ぺい
return 0; //仮に0を返す ※多態性により用いられない
}
}
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()); //鳴き声は多態性により「犬」になるが、足の数は変わらず4のまま
}
}
p.235 プロパティのオーバーロード
・シグニチャ(プロパティ名)が同じプロパティを派生クラスに定義してオーバーライドすることが可能 ・多態性(ポリモフィズム)が発生する
アレンジ演習:p.236 override03.cs
・プロパティにおいても、名前の隠ぺいでは(オーバーライドは異なり)多態性が起こらないことを確認しよう ・MammalクラスのLeg()メソッドをプロパティ(getのみ)に変更する。 ・Dogクラスに名前を隠ぺいするLeg()プロパティ(getのみ)を追加し「return 0;」としてみよう
作成例
//アレンジ演習:p.236 override03.cs
using System;
class Mammal { //基本クラス「哺乳類」
protected const int LegNo = 4; //継承可能なインスタンス変数(定数)
public virtual string Nakigoe { //オーバーライド可能な仮想プロパティ
get {
return "...";
}
}
public int Leg { //【変更】足の数を返す通常プロパティ
get { //【追加】
return LegNo; //定数値を返す
}
}
}
class Cat : Mammal { //派生クラス「猫」
public override string Nakigoe { //オーバーライドプロパティ
get {
return "ニャー、ニャー";
}
}
}
class Dog : Mammal { //派生クラス「犬」
public override string Nakigoe { //オーバーライドプロパティ
get {
return "ワン、ワン";
}
}
new public int Leg { //【以下追加】足の数を返すプロパティの名前の隠ぺい
get {
return 0; //定数値を返す ※多態性により用いられない
}
}
}
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); //【変更】鳴き声は多態性により「犬」になる、足の数は変わらず4のまま
//【以下参考までに追加】
Console.WriteLine("dog.Leg = {0}", dog.Leg); //名前の隠ぺいをするプロパティが呼ばれる
}
}
p.237 インデクサのオーバーロード
・シグニチャ(引数の型、数、並び)が同じインデクサを派生クラスに定義してオーバーライドすることが可能 ・多態性(ポリモフィズム)が発生する
アレンジ演習:p.237 override04.cs
・インデクサにおいても、名前の隠ぺいでは(オーバーライドは異なり)多態性が起こらないことを確認しよう ・MammalクラスのLeg()メソッドをthis[int]インデクサに変更する。 ・Dogクラスに名前を隠ぺいするthis[int]インデクサを追加し「return 0;」としてみよう ・なお、このプログラムのswitch構文では、必須なはずのbreakがreturnがあれば不要(記述不可)なことが分かる
作成例
//アレンジ演習:p.237 override04.cs
using System;
class Mammal { //基本クラス「哺乳類」
protected const int LegNo = 4; //継承可能なインスタンス変数(定数)
protected string Tail, Gei, Food, Koe; //継承可能なインスタンス変数
public virtual string this[string index] { //仮想インデクサ[string]
get {
return "...";
}
}
public int this[int index] { //【変更】脚の数を返す通常インデクサ[int]
get { //【追加】
return LegNo; //定数値を返す ※多態性により用いられない
}
}
}
class Cat : Mammal { //派生クラス「猫」
public override string this[string index] { //オーバライドインデクサ[string]
get {
switch (index) {
case "尾":
Tail = "1本";
return Tail; //retrunがあればbreakは不要
case "芸":
Gei = "できない";
return Gei; //retrunがあればbreakは不要
case "鳴き声":
Koe = "ニャー、ニャー";
return Koe; //retrunがあればbreakは不要
case "食べ物":
Food = "キャットフード";
return Food; //retrunがあればbreakは不要
default:
return ""; //retrunがあればbreakは不要
}
}
}
}
class Dog : Mammal{ //派生クラス「犬」
public override string this[string index] { //オーバライドインデクサ[string]
get {
switch (index) {
case "尾":
Tail = "1本";
return Tail;
case "芸":
Gei = "できる";
return Gei;
case "鳴き声":
Koe = "ワン、ワン";
return Koe;
case "食べ物":
Food = "ドッグフード";
return Food;
default:
return "";
}
}
}
new public int this[int index] { //【以下追加】脚の数を返す通常インデクサ[int]の名前の隠ぺい
get {
return 0;
}
}
}
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[0], m["尾"], m["芸"], m["食べ物"]); //【変更】脚以外は多態性により「猫」になる
m = dog; //「犬」のインスタンスを「哺乳類」型の変数で扱う
Console.WriteLine("犬の脚は{0}本です。尾は{1}です。芸は{2}。食べ物は{3}。",
m[0], m["尾"], m["芸"], m["食べ物"]); //【変更】脚以外は多態性により「犬」になる
//【以下参考までに追加】
Console.WriteLine("dog.Leg = {0}", dog[0]); //名前の隠ぺいをするインデクサが呼ばれる
}
}
p.240 クラスの多層階層
・派生クラスを基本クラスとして、さらに派生クラスを定義できる ・基本クラスのメンバは派生の派生クラスに継承される ・派生の派生クラスのオブジェクトも基本クラスの変数で扱える ・派生クラスで独自に記述したメソッドも、仮想メソッドにすることで、派生の派生クラスでオーバーライドできる ・派生クラスのオーバーライドメソッドを、そのまま、派生の派生クラスでオーバーライドできる ・つまり、オーバーライドメソッドの対象は上位のクラスの仮想メソッドまたはオーバーライドメソッド
アレンジ演習:p.240 inheritance05.cs
・派生クラスのオーバーライドメソッドを、そのまま、派生の派生クラスでオーバーライドできることを確認しよう ・Derived2クラスを継承するDerived3クラスを定義して、Show()メソッドをオーバーライドし、Main()で呼び出てみよう
提出:アレンジ演習:p.240 inheritance05.cs