p.243 クラスの継承とコンストラクタ(引数なしの場合)
・どんなクラスであっても、コンストラクタは省略可能
・省略すると引数のない&中身のないコンストラクタが自動的に用意される
・基本クラスのオブジェクトを引数のないnewで生成すると、その引数のないコンストラクタが呼ばれる
・派生クラスのオブジェクトを引数のないnewで生成すると:
①基本クラスの引数のないコンストラクタが呼ばれる
②派生クラスの引数のないコンストラクタが呼ばれる
・こうすることで、基本クラスで定義したデータメンバの初期化や基本クラス用の準備作業をコンストラクタに書いておくことにより、
派生クラスのオブジェクト生成時に自動的に実行される
・例:
class Slime { //基本クラス
public int hp;
public Slime { hp = 10; } //コンストラクタ①
}
class HoimiSlime : Slime { //派生クラス
//ここに「public int hp」があるとみなされる
public int mp;
public HoimiSlime { mp = 20; } //コンストラクタ②
}
class Game {
public play() {
HoimiSlime hoimin = new HoimiSlime(); //①②が動作してhpもmpも初期化される
:
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 クラスの継承とコンストラクタ(引数ありの場合)
・p.243 inheritance06.csの基本クラスのコンストラクタ①にint型引数iを与えて、xの初期値に用いるとすると、 派生クラスのコンストラクタ②がエラーになる ・これは派生クラスにおいて基本クラスのコンストラクタが動作できる情報を与える必要があるから ・これを解決するのがbaseキーワードで、派生クラスのコンストラクタ②に基本クラスと同じint型引数iを与えて 「:base(引数)」を指定すると良い ・そして、派生クラスのコンストラクタ②においてint型引数iをxの初期値に用いても良い ・派生の派生クラスにおいても同様 ・呼び出す側では、派生クラスのコンストラクタ②においてint型引数iをxの初期値に用いるかどうかにかかわらず、 引数を与えること
アレンジ演習:p.243 inheritance06.cs
・上記を試そう
作成例
//アレンジ演習:p.243 inheritance06.cs
using System;
class MyBase { //基本クラス
protected int x; //非公開で継承可能なデータメンバ
public MyBase(int i) { //コンストラクタ①
Console.WriteLine("ここはMyBase");
x = i;
}
}
class Derived1 : MyBase { //派生クラス
//ここに「protected int x;」があるとみなされる
public Derived1(int i) : base(i) { //コンストラクタ②
Console.WriteLine("ここはDerived1");
x = i;
}
}
class Derived2 : Derived1 { //派生の派生クラス
//ここに「protected int x;」があるとみなされる
public Derived2(int i) : base(i){ //コンストラクタ③
Console.WriteLine("ここはDerived2");
x = i;
}
public void show() {
Console.WriteLine("x = {0}", x);
}
}
class inheritance06 {
public static void Main() {
Derived2 d2 = new Derived2(30); //コンストラクタ①②③の順に動作する
d2.show(); //③で代入した30が表示
}
}
p.245 inheritance07.cs
//p.245 inheritance07.cs
using System;
class MyBase { //基本クラス
protected double d; //非公開で継承可能なデータメンバ
public MyBase(double a, double b, double c) { //コンストラクタ①
d = Math.Pow(b, 2.0) - 4.0 * a * c;
}
}
class MyJudge : MyBase { //派生クラス
//ここに「protected double d;」があるとみなされる
public bool bJudge;
public MyJudge(double p, double q, double r) : base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す
//ここで基本クラスのコンストラクタMyBase(p, q, r)が実行され、dの値が決まる
Console.WriteLine("判別式 = {0}", d);
if (d < 0.0) {
bJudge = false;
} else {
bJudge = true;
}
}
}
class inheritance07 {
public static void Main() {
MyJudge mj = new MyJudge(1.0, 2.0, 3.0); //
Console.WriteLine(mj.bJudge);
MyJudge mk = new MyJudge(1.0, 4.0, 0.0);
Console.WriteLine(mk.bJudge);
}
}
p.247(抽象メソッド)
・基本クラスにおいて派生クラスでオーバーライドして欲しいメソッドを定義できる ・例: Monsterクラスに「表示メソッド」を定義 ・こうすると、基本クラスでは具体的な内容を決める必要性がなくなる ・この場合に用いる仕掛けが抽象メソッドで、この定義より、派生クラスでは抽象メソッドのオーバーライドが義務になる ・書式: public/protected abstract 戻り値型 メソッド名(引数リスト); //中身は記述しない ・これにより、派生クラスを利用する側も、基本クラスにおける抽象メソッドがオーバーライドされていることを前提して活用できるのもメリット ・なお、抽象メソッドは静的メソッドにはできない
p.247 抽象クラス
・抽象メソッドを1つでも持つクラスは抽象クラスとなるので、クラス定義の冒頭にabstractを前置すること
・書式: abstract class クラス名 {…}
・例:
abstract class Monster { //抽象クラス
protected int hp;
protected abstract void show(); //抽象メソッド
}
p.248 abstract01.cs
//p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラス
public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド
}
class MyHanbetsu : MyAb { //派生クラス
public override double Hanbetsu(double a, double b, double c) { //抽象メソッドをオーバーライド
return Math.Pow(b, 2.0) - 4.0 * a * c;
}
}
class abstract01 {
public static void Main() {
MyHanbetsu h = new MyHanbetsu(); //派生クラスのインスタンスを生成
double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバーライドメソッドを呼ぶ
Console.WriteLine(d);
}
}
p.249(sealedクラス)
・継承は基本クラスの全メンバを引き継げるので、トラブルの元になる可能性もある ・そこで、継承を防ぎたい場合は、sealedキーワードを前置すれば良い ・C#システムが提供しているクラスの中にもsealedクラスがある ・なお、sealedクラスは抽象クラスにできないので、抽象メソッドは記述できない
p.250 クラスを分割定義する
・partialキーワードをクラス定義に前置することで、クラスをファイルをまたがって記述できる ・これにより、ファイルが小さくなって見通しが良くなると共に、複数人での分担や共有(グループ開発)がしやすくなる ・分割した全てのソースにおいて、partialキーワードをつけたクラス定義とすること ・ビルド時に自動的に結合されるので、異なるソースにあるメンバも利用できる ・Visual Studioの場合、1プロジェクトの中に複数のソースファイルを置けば良い ・p.250 partial01.csのように1ソースファイルの中でクラスを分割定義することも可能だが、あまり意味はない
p.250 partial01.cs
//p.250 partial01.cs
using System;
partial class MyClass { //MyClassクラスの定義①
public int x;
}
class partial01 { //partial01クラスの定義
public static void Main() {
MyClass mc = new MyClass();
mc.x = 10;
mc.Show();
}
}
partial class MyClass { //MyClassクラスの定義②
public void Show() {
Console.WriteLine("x = {0}", x);
}
}
アレンジ演習:p.250 partial01.cs
・MyClassクラスの定義②を、別ファイル(partial02.cs)としてソース分割しよう
提出:アレンジ演習:p.250 partial01.cs
・分割した別ファイル(partial02.cs)を。
※ p.251「メソッドを分割定義する」はメソッドの内容を分割クラスにまたがって記述できるわけではないので、実質的に不要であるため割愛。