p.245「inheritance07.cs」から
提出フォロー:アレンジ演習:p.243 inheritance06.cs
・3つのクラスのコンストラクタの引数をすべて「int i」にしたい ・xの初期値をiで与えるとする ・派生クラスのコンストラクタと、派生の派生クラスのコンストラクタにbaseキーワードを追加しよう ・Mainで派生の派生クラスのコンストラクタに引数で90を与えてみよう
作成例
//アレンジ演習: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(90); //派生の派生クラスのインスタンスを生成(①②③の実行)
d2.show(); //③が最後に動くので90になっている
}
}
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; //bの2乗-4acを返す
}
}
class MyJudge : MyBase { //派生クラス
public bool bJudge; //判定結果
public MyJudge(double p, double q, double r)
: base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す
//ここで「d = Math.Pow(q, 2.0) - 4.0 * p * r;」が動作する
Console.WriteLine("判別式 = {0}", d); //よって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); //派生クラスのコンストラクタに3値を渡す
Console.WriteLine(mj.bJudge); //コンストラクタで格納された判定結果を表示
MyJudge mk = new MyJudge(1.0, 4.0, 0.0); //派生クラスのコンストラクタに3値を渡す
Console.WriteLine(mk.bJudge); //コンストラクタで格納された判定結果を表示
}
}
p.247 抽象クラス(抽象メソッド)
・基本クラスの下に次々と派生クラスを作っていくと、具体的な内容を記述するのは派生クラスになり、基本クラスは共通する枠組みだけに なることが多い ・この場合に、シグニチャのみを基本クラスに記述したものが、抽象メソッドで、abstractキーワードを付けて示す。 ・書式: astruct 戻り値型 メソッド名(引数リスト); ・基本クラスで抽象メソッドを描くことで、派生クラスでオーバーライドして、その内容を書くことが義務になるので、基本クラスによって まとめて扱うことが保証されるのがメリット ・なお、抽象メソッドはインスタンスメソッドのみで、静的メソッドにはできない
p.248 抽象クラス
・抽象メソッドを1つでも含むクラスは抽象クラスとなり、abstractキーワードを付けて示す。
・書式: astruct class クラス名 {…}
・抽象メソッドは具体性がないので、抽象クラスはインスタンスの生成ができない
例:Slimeを派生クラスとする基本クラスMonsterは抽象クラスにすると良い。
p.248 abstract01.cs
//p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラス
//Hanbetsuメソッドは抽象メソッド
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(); //派生クラスのインスタンスは生成できる
//MyAb ma = new MyAb(); //抽象クラスのインスタンスは生成できない
double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバーライドメソッドを呼ぶ
Console.WriteLine(d);
}
}
アレンジ演習:p.248 abstract01.cs
・抽象クラスに通常のメソッドも記述できることを確認しよう ・また、派生クラスで抽象メソッドをしないことで派生クラスも抽象クラスになることを確認しよう ※ 派生の派生クラスを追記して、抽象メソッドをオーバーライドして、Mainで用いること
作成例
//アレンジ演習:p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラス
public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド
public virtual void show() { Console.WriteLine(this); } //通常の仮想メソッド
}
abstract class MyAb2 : MyAb { //派生クラスだが抽象クラス
//ここに「public abstract double Hanbetsu(double a, double b, double c);」があるとみなす
public override void show(){ Console.WriteLine(this); } //通常のオーバーライドメソッド
}
class MyHanbetsu : MyAb2 { //派生の派生クラス
//ここに「public override void show(){…}」があるとみなす
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(); //派生クラスのインスタンスは生成できる
//MyAb ma = new MyAb(); //抽象クラスのインスタンスは生成できない
double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバーライドメソッドを呼ぶ
Console.WriteLine(d);
h.show();
}
}
p.249(sealedクラス)
・継承は強力な機能であり、基本クラスの機能を獲得できてしまう。 ・これを防ぐ必要があるクラスは、sealedキーワードを前置してsealedクラスにすると良い ・C#が提供しているクラスの何もsealedクラスは多数ある ・当然、sealedクラスには抽象メソッドは記述できない
p.250 クラスを分割定義する
・partialキーワードをクラス定義に前置することで、クラスをファイルをまたがって記述できる ・これにより、見通しが良くなり、1クラスをグループ開発できるようになる ・分割したすべてのソースにおいてpartialキーワードが必要 ・実行時に自動的に結合されるので、どれかに記述されたメンバは、ほかの部分から利用できる ・Visul Studioでは、同じプロジェクトの中に複数のソースファイルを作成し、クラスを分割配置できることに加えて、 partialクラスを分割配置できる ・p.250 partial01.csのように、1ソース内でpartialクラスを記述することも可能(あまり意味はない)
p.250 partial01.cs
//p.250 partial01.cs
using System;
partial class MyClass { //分割したクラス定義①
public int x;
}
class partial01 {
public static void Main() {
MyClass mc = new MyClass();
mc.x = 10;
mc.Show();
}
}
partial class MyClass { //分割したクラス定義②
public void Show() {
Console.WriteLine("x = {0}", x); //分割したクラス定義①にあるデータメンバを利用可能
}
}
アレンジ演習:p.250 partial01.cs
・「分割したクラス定義②」を別ファイルにしよう ・Visual Studio 2022では自動的に「using System;」が補われる
作成例:partial01.cs
//p.250 partial01.cs
using System;
partial class MyClass { //分割したクラス定義①
public int x;
}
class partial01 {
public static void Main() {
MyClass mc = new MyClass();
mc.x = 10;
mc.Show();
}
}
作成例:CodeFile1.cs
using System;
partial class MyClass { //分割したクラス定義②
public void Show() {
Console.WriteLine("x = {0}", x); //分割したクラス定義①にあるデータメンバを利用可能
}
}
p.251 メソッドを分割定義する
・分割クラスにおいては、メソッドも分割できる ・ただし、戻り値型がvoidでアクセス修飾子がpublicであること ※ しかし、メソッドの内容を分割クラスにまたがって記述できるわけではないので実質的には不用
<自由参加課題> p.251 練習問題