講義メモ ・p.243「クラスの継承とコンストラクタ」から 提出フォロー:アレンジ演習:p.240 inheritance05.cs ・派生クラスのオーバーライドメソッドを、そのまま、派生の派生クラスでオーバーライドできることを確認しよう ・Derived2クラスを継承するDerived3クラスを定義して、Show()メソッドをオーバーライドし、Main()で呼び出てみよう 作成例 //アレンジ演習: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 virtual void show() … //仮想メソッド」が継承されるがオーバライドされる public override void show() { //オーバライドメソッド Console.WriteLine("z = {0}", z); } } class Derived3 : Derived2 { //【以下追加】派生の派生の派生クラス // ここに「protected int x = 10; //継承可能なインスタンス変数」が継承される // ここに「protected int y = 20; //継承可能なインスタンス変数」が継承される // ここに「public virtual void show() … //仮想メソッド」が継承されるがオーバライドされる public override void show() { //オーバライドメソッド Console.WriteLine("x = {0}, y = {1}", x, y); } } class inheritance05 { public static void Main() { MyBase mb; //基本クラス型の変数 Derived1 d1 = new Derived1(); //派生クラスのインスタンス Derived2 d2 = new Derived2(); //派生の派生クラスのインスタンス mb = d1; //派生クラスのインスタンスを基本クラス型で扱う mb.show(); //オーバライドなので多態性が発動し派生クラスのメソッドを実行 mb = d2; //派生の派生クラスのインスタンスを基本クラス型で扱う mb.show(); //オーバライドなので多態性が発動し派生クラスのメソッドを実行 //【以下追加】 Derived2 d3 = new Derived3(); //派生の派生の派生クラスのインスタンス d3.show(); //オーバライドメソッドを実行 } } p.243 クラスの継承とコンストラクタ ・どのクラスにおいても、コンストラクタ(p.167)を記述しないと、自動的に引数のないコンストラクタが用意される ・これは派生クラスでも同様 ・しかし、派生クラスのコンストラクタの前には自動的に基本クラスのコンストラクタが実行される ・これにより、基本クラスから継承したデータメンバの初期化を基本クラスのコンストラクタに任させることができる ・基本クラスに引数を持つコンストラクタがあり、派生クラスにも引数を持つコンストラクタがある場合、引数を受け渡す必要があるので、baseキーワードを用いる ・書式: public 派生クラス名(型 引数,…) : base (受け取りたい引数,…) {…} 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 { //派生クラス public Derived1() { //③派生クラスの引数無コンストラクタ //ここで基本クラスの引数無コンストラクタが実行される Console.WriteLine("ここはDerived1"); //⑦ x = 20; //⑧ } } class Derived2 : Derived1 { //派生の派生クラス 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(); //⑪派生の派生オブジェクトのメソッドを呼ぶ } } アレンジ演習:p.243 inheritance06.cs ・Derived1クラスにprivateなint型のインスタンス変数yを追加 ・また、yに40を代入し、その旨を表示する処理をコンストラクタに追加 作成例 //アレンジ演習:p.243 inheritance06.cs using System; class MyBase { //基本クラス protected int x; //継承可能なインスタンス変数 public MyBase() { //④基本クラスの引数無コンストラクタ Console.WriteLine("ここはMyBase"); //⑤ x = 10; //⑥ } } class Derived1 : MyBase { //派生クラス private int y; public Derived1() { //③派生クラスの引数無コンストラクタ //ここで基本クラスの引数無コンストラクタが実行される Console.WriteLine("ここはDerived1"); //⑦ x = 20; //⑧ Console.WriteLine("yに40を代入"); //【追加】 y = 40; //【追加】 } } class Derived2 : Derived1 { //派生の派生クラス 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(); //⑪派生の派生オブジェクトのメソッドを呼ぶ } } アレンジ演習:p.245 inheritance07.cs ・テキストp.246下から4行目の例のとおりに、コンストラクタを書換え、呼び出し部分などもアレンジしてみよう 作成例 //アレンジ演習: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 { //派生クラス public bool bJudge; //内部用のフラグ変数 public MyJudge(int x, double p, double q, string str, double r) //【変更】派生クラスの引数有コンストラクタ : base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す //ここで基本クラスの引数有コンストラクタが実行される(引数は↑から受け取る) Console.WriteLine("判別式 = {0}", d); //基本クラスの引数有コンストラクタによりdに代入 if (d < 0.0) { bJudge = false; } else { bJudge = true; } Console.WriteLine("x = {0}, str = {1}", x, str); //【追加】基本クラスの引数有コンストラクタによりdに代入 } } class inheritance07 { public static void Main() { MyJudge mj = new MyJudge(100, 1.0, 2.0, "OK", 3.0); //【変更】コンストラクタに5値を渡す Console.WriteLine(mj.bJudge); MyJudge mk = new MyJudge(200, 1.0, 4.0, "NG", 0.0); //【変更】コンストラクタに5値を渡す Console.WriteLine(mk.bJudge); } } p.247 抽象クラス ・継承を用いてクラスの上下関係を構築しておくと、具体的な記述は派生クラス側になり、基本クラスはクラス群を代表する存在になっていく ・例: 基本クラスはMonster、派生クラスはSlime、Dragon、その派生クラスはHoimiSlime など ・そして、基本クラスには具体的な記述をせず、派生クラスに任せる方が効率的な構造になる  ⇒企業でいえば持株会社(ホールディング)と実務子会社(カンパニー)の関係に近い ・この時に用いる「中身は派生クラスで」というメソッドが抽象メソッド ・書式: abstruct 戻り値型 メソッド名(引数型 引数,…); //抽象メソッドは{}がなく;で閉じる ・抽象メソッドを持つクラスを抽象クラスといい、クラス名の前にもabstructを付記する ・書式: abstruct class クラス名 {…} ・抽象クラスはインスタンスを生成できない ・基本クラスが抽象クラスの場合、派生クラスでは全ての抽象メソッドをオーバーライドする必要がある ・あるいは、一部または全部の抽象メソッドをオーバーライドしないことで、派生クラスを抽象クラスにできる ・例: 基本クラスはMonster(抽象)、派生クラスはSkyMonster(抽象)、その派生クラスはDragonなど p.247 抽象クラス:補足 ・p.256にある通り、抽象メソッドと同様に、抽象プロパティ、抽象インデクサを記述できる ・抽象プロパティの書式: abstruct 戻り値型 プロパティ名{get; set;}; //getとsetのどちらかは省略可 ・抽象インデクサの書式: abstruct 戻り値型 this[インデクス型 インデクス名]{get; set;}; //getとsetのどちらかは省略可 p.248 abstract01.cs //p.248 abstract01.cs using System; abstract class MyAb { //抽象クラスMyAb public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド } class MyHanbetsu : MyAb { //MyAbの派生クラス //ここに「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる 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() { //MyAb y = new MyAb(); //【確認】抽象クラスのインスタンスは生成不可 MyHanbetsu h = new MyHanbetsu(); //派生クラスのインスタンスを生成 double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバライドメソッドを実行 Console.WriteLine(d); } } アレンジ演習:p.248 abstruct01.cs ・抽象クラスMyAbに抽象プロパティage(戻り値型:int、get/set有)を追加する ・すると、MyHanbetuクラスが抽象クラスになり、そのままではエラーになる ・そこで、MyHanbetuクラスの派生クラスYourHanbetuを追記し、抽象プロパティageをオーバーライドしよう ・そして、Mainメソッドにおいて、派生クラスYourHanbetuを使うようしてエラーを解消しよう 作成例 //アレンジ演習:p.248 abstract01.cs using System; abstract class MyAb { //抽象クラスMyAb public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド public abstract int age { get; set;} //【追加】抽象プロパティ } abstract class MyHanbetsu : MyAb { //【変更】MyAbの派生抽象クラス //ここに抽象メソッド「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる public override double Hanbetsu(double a, double b, double c) { //↑のオーバライド return Math.Pow(b, 2.0) - 4.0 * a * c; } //ここに抽象プロパティ「public abstract int age { get; set;}」があるとみなされる } class YourHanbetsu : MyHanbetsu { //【以下追加】派生の派生クラス int yourage = 0; //ここに抽象メソッド「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる //ここにオーバライド「public override double Hanbetsu(double a, double b, double c) {}」があるとみなされる //ここに抽象プロパティ「public abstract int age { get; set;}」があるとみなされる public override int age { get { return yourage; } set { yourage = value;} } //↑のオーバライド } class abstract01 { public static void Main() { //MyHanbetsu h = new MyHanbetsu(); //【削除】派生クラスのインスタンスを生成 YourHanbetsu h = new YourHanbetsu(); //【差替】派生の派生クラスのインスタンスを生成 double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバライドメソッドを実行 Console.WriteLine(d); Console.WriteLine(h.age); //【追加】オーバーライドプロパティを呼ぶ } } p.249 抽象クラス:sealedクラスについて ・抽象クラスとは逆の概念で、継承を禁止するにはsealedクラスにすると良い ・継承は便利だが、publicメンバが無条件に引き継がれてしまうので、これを避けたい場合に用いる ・なお、C#が提供するクラスの中にも、sealedクラスがあり、継承しようとするとエラーになる  sealedクラスの例: Stringクラス、Int32クラス、など  sealedではないクラスの例:System.Collections.ArrayListクラス(p.177) など ・ちなみに、静的クラス(p.204)も継承できない p.250 クラスを分割定義する ・1クラスを複数のソースリストで記述できる ・これにより、ソースの長大化を防いだり、チーム開発を効率化できる ・テキストではクラス定義を2分割して1ソースに置いているが、通常、ソースの分割に用いる ・クラスを分割定義するには、classの前に partial を付記する(これを部分クラスともいう) ・部分クラスのどこかで定義したメンバは、他のソースにある部分クラスからも利用可能 ・ビルドにより分割クラスは1つになり、1つの実行ファイルが生成される ・なお、クラスを分割定義している場合は、VisualStudioの「ファイル」「すべて保存」を用いて、一部ファイルの保存モレを防止すると良い アレンジ演習:p.250 partial01.cs ・018行目までをpartial01.csに、020行目以降をpartial01a.csに分離しよう ・なお、「using System;」は両方またはどちらか片方で必要に応じて配置すること ・1つのプロジェクトにpartial01.csファイルがある状態で、そこに、ソースファイルを追加すれば良い 作成例:partial01.cs(分割元) //アレンジ演習: partial01.cs using System; partial class MyClass { //部分クラス MyClassクラス① public int x; } class partial01 { //通常クラス public static void Main() { MyClass mc = new MyClass(); //部分クラスを結合した状態で扱う mc.x = 10; mc.Show(); } } 作成例:partial01.cs(分割先) //アレンジ演習: partial01a.cs using System; partial class MyClass { //部分クラス MyClassクラス② public void Show() { Console.WriteLine("x = {0}", x); } } p.251 メソッドを分割定義する ・分割したクラスにおいて、メソッドの宣言と定義を別々の部分クラスに記述できる ・これを部分メソッドといい、メソッドの冒頭に「partial」を指定する ・ただし、戻り値型がvoidで、privateなメソッドに限る ・部分メソッド宣言の書式: partial void メソッド名(引数型 引数名, …); ・部分メソッド定義の書式: partial void メソッド名(引数型 引数名, …){…} ※ この仕掛けは未完成のメソッドがある場合に便利で、 シグニチャだけを部分メソッドとして記述しておくと良い ※ 1メソッドを複数ソースファイルで分割記述できるわけではない アレンジ演習:p.251 partialmethod01.cs ・020行目までをpartialmethod01.csに、021行目以降をpartialmethod01a.csに分離しよう ・なお、「using System;」は両方またはどちらか片方で必要に応じて配置すること ・1つのプロジェクトにpartialmethod01a.csファイルがある状態で、そこに、ソースファイルを追加すれば良い 作成例:partialmethod01.cs(分割元) // partialmethod01.cs using System; partial class Partialmethod01 { public static void Main() { Console.WriteLine("Mainメソッドです"); Partialmethod01 pm = new Partialmethod01(); pm.Func1(); pm.Func2("テスト"); } //ここではメソッドの中身を記述していない //戻り値はvoid型、privateでなくてはだめ partial void Func1(); //宣言のみ partial void Func2(string s); //宣言のみ } 作成例:partialmethod01a.cs(分割先) using System; partial class Partialmethod01 { partial void Func1(){ //定義を含む Console.WriteLine("パーシャルメソッド1です"); } } partial class Partialmethod01 { partial void Func2(string s) { //定義を含む Console.WriteLine("引数は「{0}」です", s); } } p.254 ex0901.cs/ex0902.cs 練習問題1,2:ヒント ・p.231 override01.csをベースにすると良い ・Mainメソッドの作例: A2 a2 = new A2(); //派生クラスのインスタンスを生成 Console.WriteLine(a2.Calc(5, 8)); //差を表示? A1 a1 = a2; //基本クラス型の変数にインスタンスを代入 Console.WriteLine(a1.Calc(5, 8)); //和を表示? 差を表示? 作成例: 練習問題1 //p.254 ex0901.cs 練習問題1 using System; class A1 { //基本クラス public virtual int Calc(int x, int y) { //オーバーライドを許可する仮想メソッド return x + y; //和を返す } } class A2 : A1 { //派生クラス //ここに「public virtual int Calc(int x, int y){…}」があるが、下記にオーバーライドされる public override int Calc(int x, int y) { //オーバーライドメソッド return (x > y) ? x - y : y - x; //差を返す } } class ex0901 { public static void Main() { A2 a2 = new A2(); //派生クラスのインスタンスを生成 Console.WriteLine("a2.Calc(10, 30) = {0}", a2.Calc(10, 30)); //差になる A1 a1 = a2; //基本クラスの整数にインスタンスを渡す Console.WriteLine("a1.Calc(10, 30) = {0}", a1.Calc(10, 30)); //多態性で差になる } } 作成例: 練習問題2 //p.254 ex0902.cs 練習問題2 using System; class A1 { //基本クラス public int Calc(int x, int y) { //通常メソッド(継承可能) return x + y; //和を返す } } class A2 : A1 { //派生クラス //ここに「public int Calc(int x, int y){…}」があるが、下記に「名前の隠ぺい」される new public int Calc(int x, int y) { //名前の隠ぺい return (x > y) ? x - y : y - x; //差を返す } } class ex0901 { public static void Main() { A2 a2 = new A2(); //派生クラスのインスタンスを生成 Console.WriteLine("a2.Calc(10, 30) = {0}", a2.Calc(10, 30)); //差になる A1 a1 = a2; //基本クラスの整数にインスタンスを渡す Console.WriteLine("a1.Calc(10, 30) = {0}", a1.Calc(10, 30)); //多態性がなく和になる } } 第10章 インターフェイス p.255 インターフェイス ・元の意味は「接合部/面」で「異なる要素を関連付ける仕掛け」 ・C#では、継承関係にないクラスであっても、関連付けられる仕組み ・例えば、空中シューティングゲームで、飛んでいるドラゴンとF16戦闘機を「飛べるもの」インターフェイスでまとめて扱うことができる ・インターフェイスは文法では抽象クラスの拡張形式で「抽象メンバのみをもった参照型」となる ・書式: interface インターフェイス名 { 抽象メンバ … } ・インターフェイスに記述できる抽象メンバとしては、抽象メソッド、抽象プロパティ、抽象インデクサがある  ※ (Javaとは異なり)データメンバは記述できない ・例:  interface Flyable { string HowToFly(); } //「飛べるもの」インターフェイスに飛び方を返すメソッドがある ・なお、インターフェイスの中に記述した抽象メンバは「abstract」は不要 ・インターフェイスの中の抽象メソッドの書式: 戻り値型 メソッド名(引数型 引数,…); ・インターフェイスの中の抽象プロパティの書式: データ型 プロパティ名 { get; set; } ・インターフェイスの中の抽象インデクサの書式: データ型 this[インデックス型 インデックス名 { get; set; } p.256 インターフェイスの実装 ・クラスの継承と同じ形式で、インターフェイスの実装が可能 ・なお、クラスの継承と異なり、インターフェイスの実装は複数行って良い ・書式: class クラス名 : インターフェイス名,… {…} ・例:  class F16 : Flyable {…} //F16戦闘機は「飛べるもの」 ・例:  class Dragon : Flyable, Swimable {…} //ドラゴンは「飛べるもの」で「泳げるもの」 ・インターフェイスを実装したクラスでは、インターフェイスにある抽象メンバをオーバーライドする必要がある ・しかし、これは継承におけるオーバーライドとは区別され、override指定は不要で、通常、publicにする ・例:  class F16 : Flyable { //F16戦闘機は「飛べるもの」   public string HowToFly() {    return "エンジンで";   }  } ・例:  class Dragon : Flyable, Swimable { //ドラゴンは「飛べるもの」で「泳げるもの」   public string HowToFly() {    return "自分の翼で";   }   public string HowToSwim() {    return "自分の尾で";   }  } アレンジ演習:p.257 interface01.cs ・IMyInterfaceインターフェイスを実装するYourClassを追加し、MainメソッドでMyClassと同様に扱えることを試そう 提出:アレンジ演習:p.257 interface01.cs 次回(最終回)予告:p.259 1つのインターフェイスを複数のクラスで実装する