・p.223「第9章 継承」から
提出フォロー p.222 練習問題2 ヒント ex0802.cs
・p.217 indexer03.csをベースにすると良い ・コンストラクタで生徒数を入力する部分は、p.214 indexer02.csが参考になる ・生徒名を格納するstring型配列と、点数を格納するint型配列を宣言して用いると楽 ※ 生徒名と点数をメンバとするクラスオブジェクトの配列にできればさらに良い ・要素の生成はコンストラクタで行うと良い ・Mainメソッドで格納結果を表示すること
作成例
//p.222 練習問題2 ex0802.cs
using System;
class MyIndexer {
int[] point; //点数の配列の宣言
string[] name; //名前の配列の宣言
int num; //要素数(メソッドで設定)
public int this[string Name] { //文字列をインデックスとするインデクサ①
get {
for (int i = 0; i < num; i++) { //配列の全要素について
if (Name == name[i]) { //名前と一致?
return point[i]; //点数を返す
}
}
return -1; //一つも一致しなかったら-1を返す(0点だとあり得るので)
}
set {
for (int i = 0; i < num; i++) { //配列の全要素について
if (Name == name[i]) { //名前と一致?
point[i] = value; //点数を格納
}
}
}
}
public string this[int i] { //添字をインデックスとするインデクサ②
get {
return name[i]; //名前[i]を返す
}
set {
name[i] = value; //名前[i]に格納
}
}
public MyIndexer(int i) { //要素数を受け取るコンストラクタ
point = new int[i]; //引数で受け取った要素数で点数の配列本体を生成
name = new string[i]; //引数で受け取った要素数で名前の配列本体を生成
num = i; //引数で受け取った要素数をデータメンバに格納
}
}
class indexer03 {
public static void Main() {
const int N = 3; //要素数
MyIndexer mi = new MyIndexer(N); //要素数Nで生成
mi[0] = "マチュ"; //インデクサ②のsetを呼ぶ
mi[1] = "ニャアン"; //インデクサ②のsetを呼ぶ
mi[2] = "ハロ"; //インデクサ②のsetを呼ぶ
mi["マチュ"] = 60; //インデクサ①のsetを呼ぶ
mi["ニャアン"] = 70; //インデクサ①のsetを呼ぶ
mi["ハロ"] = 80; //インデクサ①のsetを呼ぶ
for (int i = 0; i < N; i++) { //全要素について繰返す
string name = mi[i]; //インデクサ②のgetを呼ぶ
int point = mi[name]; //インデクサ①のgetを呼ぶ
Console.WriteLine("{0} : {1}点", name, point); //インデクサ①②の組合せで名前と点数を得る
}
}
}
アレンジ演習:p.224 inheritance01.cs
・派生クラスの中に継承されている基本クラスのメンバをコメントで示そう
作成例
//アレンジ演習:p.224 inheritance01.cs
using System;
//基本クラス
class MyBase {
public int a = 10; //インスタンス変数
public void BaseMethod() { //インスタンスメソッド
Console.WriteLine("ここは基本クラスです");
}
}
//派生クラス
class MyDerived : MyBase { //MyBaseを基本クラスとする派生クラス
//public int a = 10; //インスタンス変数が継承される
public int b = 20; //インスタンス変数
public void DerivedMethod() { //インスタンスメソッド
Console.WriteLine("ここは派生クラスです");
}
// public void BaseMethod() { //インスタンスメソッドが継承される
// Console.WriteLine("ここは基本クラスです");
// }
}
class inheritance01 {
public static void Main() {
MyDerived md = new MyDerived(); //派生クラスのオブジェクトを生成
md.BaseMethod(); //派生クラスが継承した基本クラスのメソッドを呼ぶ
md.DerivedMethod(); //派生クラスのメソッドを呼ぶ
Console.WriteLine("md.a = {0}", md.a); //派生クラスが継承した基本クラスのデータ
Console.WriteLine("md.b = {0}", md.b); //派生クラスのデータ
MyBase mb = new MyBase(); //基本クラス
mb.BaseMethod(); //基本クラスのメソッドを呼ぶ
Console.WriteLine("mb.a = {0}", mb.a); //基本クラスのデータ
}
}
p.226 protectedによるデータの保護
・基本クラスにおけるメンバの継承可をアクセス修飾子のpublicで行うと、外部から自由に利用できてしまい良くない ・そこで、アクセス権限はprivateだが、継承は可であるメンバはprotected指定にすると良い
アレンジ演習:p.226 inheritance02.cs
・Mainで「md.x = 30;」とするとエラーになることを確認しよう
作成例
//アレンジ演習:p.226 inheritance02.cs
using System;
class Base { //基本クラス
protected int x = 20; //外部からは直接利用不可だが、継承は可能
}
class Derived : Base { //派生クラス
//protected int x = 20; //ここにあるとみなす(外部からは直接利用不可だが、継承は可能)
int y = 10;
public void ShowXY() {
// このクラスからはBaseクラスのxは見える
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
class inheritance02 {
public static void Main() {
Derived md = new Derived();
// md.x = 30; //protected指定なので外部からはアクセスできない
md.ShowXY();
}
}
p.228 名前の隠蔽
・派生クラスで基本クラスと同じ名前のデータメンバや、シグニチャ(名前と引数の型・数・並び)が同じメソッドを記述できる ・すると、名前の隠蔽が起こり、派生クラスにおける定義が優先される(見かけ上、上書きになる) ・名前の隠蔽が起こっていることを派生クラスのメンバの定義の先頭に「new」を記述することで明示できる(明示が推奨される)
アレンジ演習:inheritance03.cs
・Mainメソッドで基本クラスのオブジェクトを生成し、名前の隠蔽がされているメンバが利用可能なことを確認しよう
作成例
//アレンジ演習:p.228 inheritance03.cs
using System;
class Base { //基本クラス
public int x = 10; //継承により隠ぺいされるインスタンス変数
public void BaseMethod() { //継承により隠ぺいされるインスタンスメソッド
Console.WriteLine("Baseクラスです");
}
}
class Derived : Base
{
// ここに「public int x = 10;」が継承されるが、次の定義で名前の隠ぺいになる
new public int x = 20; //名前の隠ぺいになるインスタンス変数の定義
new public void BaseMethod() { //名前の隠ぺいになるメソッドの定義
Console.WriteLine("Derivedクラスです");
}
}
class inheritance03 {
public static void Main() {
Derived d = new Derived();
Console.WriteLine("x = {0}", d.x); //名前を隠ぺいしている側のインスタンス変数
d.BaseMethod(); //同上のインスタンスメソッド
//【以下追加】
Base b = new Base(); //基本クラス
Console.WriteLine("x = {0}", b.x); //名前を隠ぺいされている側のインスタンス変数
b.BaseMethod(); //同上のインスタンスメソッド
}
}
p.229 名前の隠蔽:baseキーワード
・「base.」を記述することで、隠蔽されたメンバを、派生クラスの中で利用することができる
アレンジ演習:p.228 inheritance04.cs
・「base.」を記述することで、隠蔽されたメンバを、派生クラスの中で利用することができるが、 隠蔽されたデータメンバの値を、派生クラスの中で書き換えるとどうなるか確認しよう
作成例
//アレンジ演習:p.229 inheritance04.cs
using System;
class Base { //基本クラス
protected int x = 10; //継承可能なインスタンス変数(外部からの利用は不可)
}
class Derived : Base {
// protected int x = 10; //インスタンス変数が継承されるが、直後に隠ぺいされる
new int x = 20; //名前の隠ぺいになるインスタンス変数の定義
public void Show() { //派生クラスの自前のメソッド
base.x = 30; //【追加】継承したデータメンバの値を書き換える
Console.WriteLine("base.x = {0}, x = {1} ", base.x, x); //隠ぺいされた基本クラスの変数値
}
}
class inheritance04 {
public static void Main() {
Derived d = new Derived(); //派生クラスのインスタンスを生成
d.Show(); //派生クラスの自前のメソッドを呼ぶ(値の書き換えがされている)
}
}
p.230 メソッドのオーバーライド
・メソッドにおいては名前の隠蔽とオーバーライドの2パターンから選ぶことができる ・オーバーライドを選ぶと、p.232で後述する多態性(ポリモフィズム)が実現し、オブジェクトの扱い方を効率化できるので、 一般にメソッドにおいては名前の隠蔽ではなくオーバーライドを用いることが多い ・ただし、オーバーライドにおいては、基本クラス側でオーバーライドを許可する必要がある ・基本クラス側でオーバーライドを許可するにはメソッドに「virtual」をつけて仮想メソッドにする ・派生クラス側でオーバーライドするメソッドには「override」をつけてオーバーライドメソッドにする
アレンジ演習:p.213 override01.cs
・Mainメソッドで基本クラスのオブジェクトを生成し、オーバーライドされているvirtualメソッドが利用可能かどうか確認しよう
作成例
//アレンジ演習:p.231 override01.cs
using System;
class MyBase { //基本クラス
public virtual void Method() { //オーバーライドを許可する仮想メソッド
Console.WriteLine("MyBase");
}
}
class Derived1 : MyBase { //派生クラス①
//ここに「public virtual void Method(){…}」があるが、下記にオーバーライドされる
public override void Method() { //オーバーライドメソッド①
Console.WriteLine("Derived1");
}
}
class Derived2 : MyBase { //派生クラス②
//ここに「public virtual void Method(){…}」があるが、下記にオーバーライドされる
public override void Method() { //オーバーライドメソッド②
Console.WriteLine("Derived2");
}
}
class override01 {
public static void Main() {
Derived1 d1 = new Derived1(); //派生クラス①
Derived2 d2 = new Derived2(); //派生クラス②
d1.Method(); //オーバーライドメソッド①を呼ぶ
d2.Method(); //オーバーライドメソッド②を呼ぶ
//【以下追加】
MyBase m1 = new MyBase(); //基本クラス
m1.Method(); //Virtualメソッドを呼ぶ
}
}