講義メモ ・p.243「クラスの継承とコンストラクタ」から p.243 クラスの継承とコンストラクタ ・どのクラスにおいても、コンストラクタを記述しないと、自動的に引数のないコンストラクタが用意される ・これは派生クラスでも同様 ・しかし、派生クラスのコンストラクタの前には自動的に基本クラスのコンストラクタが実行される ・これにより、基本クラスから継承したデータメンバの初期化を基本クラスのコンストラクタに任させることができる ・基本クラスに引数を持つコンストラクタがあり、派生クラスにも引数を持つコンストラクタがある場合、引数を受け渡す必要があるので、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 { //派生クラス //ここに「protected int x;」があるとみなされる public Derived1() { //コンストラクタ(引数なし) //ここでMyBaseのコンストラクタを実行 Console.WriteLine("ここはDerived1"); x = 20; //データメンバの初期化 } } class Derived2 : Derived1 { //派生の派生クラス //ここに「protected int x;」があるとみなされる public Derived2() { //コンストラクタ(引数なし) //ここでDerived1のコンストラクタを実行(中で) Console.WriteLine("ここはDerived2"); x = 30; //データメンバの初期化 } public void show() { //独自のメソッド Console.WriteLine("x = {0}", x); } } class inheritance06 { public static void Main() { Derived2 d2 = new Derived2(); //派生の派生クラスのコンストラクタを実行(3つのコンストラクタが動作) d2.show(); //x = 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 { //派生クラス public bool bJudge; public MyJudge(double p, double q, double r) //コンストラクタ(引数あり) : base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す(p→a,q→b,r→c) //ここに「protected double d;」があるとみなされる //ここでMyBaseのコンストラクタ(p, q, r)を実行 //つまり、ここに d = Math.Pow(q, 2.0) - 4.0 * p * r; があるとみなされる 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、派生クラスはSlime、Dragon、その派生クラスはHoimiSlime など ・そして、基本クラスには具体的な記述をせず、派生クラスに任せる方が効率的な構造になる ・この時に用いる「中身は派生クラスで」というメソッドが抽象メソッド ・書式: abstruct 戻り値型 メソッド名(引数型 引数,…); //抽象メソッドは{}がなく;で閉じる ・抽象メソッドを持つクラスを抽象クラスといい、クラス名の前にもabstructを付記する ・書式: abstruct class クラス名 {…} ・抽象クラスはインスタンスを生成できない ・基本クラスが抽象クラスの場合、派生クラスでは全ての抽象メソッドをオーバーライドする必要がある ・あるいは、一部または全部の抽象メソッドをオーバーライドしないことで、派生クラスを抽象クラスにできる ・例: 基本クラスはMonster(抽象)、派生クラスはSkyMonster(抽象)、その派生クラスはDragonなど 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クラスにすれば良い ・書式: sealed class クラス名 {…} ・なお、C#が提供するクラスの中にも、sealedクラスが含まれている p.250 クラスを分割定義する ・1クラスを複数のソースリストで記述できる ・これにより、ソースの長大化を防いだり、チーム開発を効率化できる ・テキストではクラス定義を2分割して1ソースに置いているが、通常、ソースの分割に用いる ・クラスを分割定義するには、classの前に partial を付記する(これを部分クラスともいう) ・部分クラスのどこかで定義したメンバは、他のソースにある部分クラスからも利用可能 ・ビルドにより分割クラスは1つになり、1つの実行ファイルが生成される アレンジ演習:p.250 partial01.cs ・018行目までをpartial01.csに、020行目以降をpartial01a.csに分離しよう ・なお、「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(); //部分クラスの続きにあるメソッド } } 作成例 partial01a.cs //アレンジ演習:p.250 partial01a.cs using System; partial class MyClass { //部分クラスの続き public void Show() { //メソッドはこちらへ Console.WriteLine("x = {0}", x); //他の部分クラスにあるデータメンバを利用可 } } p.251 メソッドを分割定義する ・分割したクラスにおいて、メソッドの定義とメソッドの内容を別々の部分クラスに記述できる ・これを部分メソッドという ・ただし、戻り値型がvoidで、privateなメソッドに限る ※ この仕掛は未完成のメソッドがある場合に便利で、 シグニチャだけを部分メソッドとして記述しておくと良い ※ 1メソッドを複数ソースファイルで記述できるわけではない p.251 partialmethod01.cs //p.251 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); //部分メソッド②の宣言のみ } partial class Partialmethod01 { //部分クラス② partial void Func1(){ //部分メソッド①の内容を記述 Console.WriteLine("パーシャルメソッド1です"); } } partial class Partialmethod01 { //部分クラス③ partial void Func2(string s) { //部分メソッド②の内容を記述 Console.WriteLine("引数は「{0}」です", s); } } p.254 練習問題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)); //和を表示? 差を表示? 第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 { public string HowToFly() { return "エンジンで";} } //F16戦闘機は「飛べるもの」 作成例 //p.257 interface01.cs using System; interface IMyInterface { //インターフェイスの定義 void show(string str); //抽象メソッド int xprop { get; set; } //抽象プロパティ int this[int i] { get; set; } //抽象インデクサ } class MyClass : IMyInterface { //インターフェイスを実装するクラスの定義 int i; //データメンバ int[] arr = new int[10]; //データメンバである配列 public void show(string str) { //抽象メソッドのオーバーライド Console.WriteLine(str); } public int xprop { //抽象プロパティのオーバーライド get { return i; } set { i = value; } } public int this[int index] { //抽象インデクサのオーバーライド get { return arr[index]; } set { arr[index] = value; } } } class interface01 { public static void Main() { MyClass mc = new MyClass(); //インターフェイスを実装するクラスのインスタンス生成 mc.show("Test Interface"); //抽象メソッドのオーバーライドを呼ぶ mc.xprop = 100; //抽象プロパティのオーバーライド(set)を呼ぶ Console.WriteLine("mc.xprop = {0}", mc.xprop); //抽象プロパティのオーバーライド(get)を呼ぶ for (int i = 0; i < 10; i++) { mc[i] = i * 2; //抽象インデクサのオーバーライド(set)を呼ぶ } for (int i = 0; i < 10; i++) { Console.WriteLine("mc[{0}] = {1}", i, mc[i]); //抽象インデクサのオーバーライド(get)を呼ぶ } } } p.259 1つのインターフェイスを複数のクラスで実装する ・継承関係にないクラスであっても、同じインターフェイスを実装していれば、関連付けられる p.259 interface02.cs //p.259 interface02.cs using System; interface IMyInterface { //インターフェイスの定義 void show(string str); //抽象メソッド } class MyClass : IMyInterface { //インターフェイスを実装するクラス① public void show(string s) { //抽象メソッドのオーバーライド① Console.WriteLine(s); } } class YourClass : IMyInterface { //インターフェイスを実装するクラス② public void show(string x) { //抽象メソッドのオーバーライド② Console.WriteLine("{0}が入力されました", x); } } class interface02 { public static void Main() { MyClass mc = new MyClass(); //インターフェイスを実装するクラス①のインスタンス生成 YourClass yc = new YourClass(); //〃②のインスタンス生成 mc.show("abc"); //抽象メソッドのオーバーライド①を呼ぶ yc.show("abc"); //抽象メソッドのオーバーライド②を呼ぶ } } p.260 1つのインターフェイスを複数のクラスで実装する つづき ・クラスを型とする変数が定義できるように、インターフェイスを型とする変数が定義できる ・ただし、インターフェイスは抽象なのでインスタンスを生成できない ・インターフェイスを実装したクラスのオブジェクトを、インターフェイスを型とする変数で扱える ・例:  Dragon Veldra = new Dragon(); //ドラゴンのヴェルドラを生成  Flyable obj1 = Veldra; //ヴェルドラを飛行物体1とする  F16 Masotan = new F16(); //F16戦闘機のマソタンを生成  Flyable obj2 = Masotan; //マソタンを飛行物体2とする p.261 interface03.cs //p.261 interface03.cs using System; interface IMyInterface { //インターフェイスの定義 int calc(int x, int y); //抽象メソッド「xとyで計算した結果を返す」 } class Plus : IMyInterface { //インターフェイスを実装したクラス① public int calc(int a, int b) { //抽象メソッドをオーバーライドして① return a + b; //「aとbで計算した結果を返す」→「aとbの和を返す」とした } } class Minus : IMyInterface { //インターフェイスを実装したクラス② public int calc(int a, int b) { //抽象メソッドをオーバーライドして② return a - b; //「aとbで計算した結果を返す」→「a引くbを返す」とした } } class interface03 { public static void Main() { IMyInterface im; //インターフェイス型の変数 Plus p = new Plus(); //インターフェイスを実装したクラス①のオブジェクト① Minus m = new Minus(); //インターフェイスを実装したクラス②のオブジェクト② im = p; //オブジェクト①をインターフェイス型の変数に代入 Console.WriteLine("im.calc = {0}", im.calc(3, 5)); //インターフェイス型の変数経由でメソッド①を im = m; //オブジェクト②をインターフェイス型の変数に代入 Console.WriteLine("im.calc = {0}", im.calc(3, 5)); //インターフェイス型の変数経由でメソッド②を } } アレンジ演習:p.261 interface03.cs ・インターフェイス型の配列の要素にできることを確認しよう 作成例 //アレンジ演習:p.261 interface03.cs using System; interface IMyInterface { //インターフェイスの定義 int calc(int x, int y); //抽象メソッド「xとyで計算した結果を返す」 } class Plus : IMyInterface { //インターフェイスを実装したクラス① public int calc(int a, int b) { //抽象メソッドをオーバーライドして① return a + b; //「aとbで計算した結果を返す」→「aとbの和を返す」とした } } class Minus : IMyInterface { //インターフェイスを実装したクラス② public int calc(int a, int b) { //抽象メソッドをオーバーライドして② return a - b; //「aとbで計算した結果を返す」→「a引くbを返す」とした } } class interface03 { public static void Main() { IMyInterface[] im = new IMyInterface[2]; //インターフェイス型の配列 Plus p = new Plus(); //インターフェイスを実装したクラス①のオブジェクト① Minus m = new Minus(); //インターフェイスを実装したクラス②のオブジェクト② im[0] = p; //オブジェクト①をインターフェイス型の配列[0]に代入 im[1] = m; //オブジェクト②をインターフェイス型の配列[1]に代入 foreach (var i in im) { Console.WriteLine("im.calc = {0}", i.calc(3, 5)); //インターフェイス型の変数経由でメソッド①②を } } } p.263 複数のインターフェイスを実装する ・クラスの継承と異なり、インターフェイスの実装は複数行って良い 正誤:p.263 interface04.cs 010行目 ・「: IFirst」は無用(インターフェイスの継承(p.266)になってしまう) p.263 interface04.cs //p.263 interface04.cs using System; interface IFirst { //インターフェイス①の定義 void show(int x); //抽象メソッド① } interface ISecond { //インターフェイス②の定義 void show(int x, int y); //抽象メソッド② } class MyClass : IFirst, ISecond { //インターフェイス①②を実装するクラス public void show(int x) { //抽象メソッド①のオーバーライド Console.WriteLine("x = {0}", x); } public void show(int x, int y) { //抽象メソッド②のオーバーライドで、オーバーロードでもある Console.WriteLine("x = {0}, y = {1}", x, y); } } class interface04 { public static void Main() { MyClass mc = new MyClass(); mc.show(2); //抽象メソッド①のオーバーライドを呼ぶ mc.show(1, 3); //抽象メソッド②のオーバーライドを呼ぶ } } p.264 複数のインターフェイスを実装する・つづき ・複数のインターフェイスを実装する場合に、双方に同じシグニチャのメソッドがあるとエラーにはならないが、オーバーライドにおいて区別する必要がある ・定義のメソッド名の前に「インターフェイス名.」を付記すれば良い ・書式: 戻り値型 インターフェイス名.メソッド名(引数型 引数名,…) {…} ・なお、この場合「public」は不要 正誤:p.264 interface05.cs 005行目,010行目 ・どちらも先頭の「public」は無用 p.264 interface05.cs //p.264 interface05.cs using System; public interface IMas { //インターフェース①「ます」 void show(int i); //抽象メソッド① } public interface IDa { //インターフェース②「だ」 void show(int i); //抽象メソッド②(※シグニチャが①と同じ) } class MyClass : IMas, IDa { //インターフェイス①②を実装するクラス void IMas.show(int i) { //抽象メソッド①のオーバーライド Console.WriteLine("iは{0}です", i); } void IDa.show(int i) { //抽象メソッド②のオーバーライド Console.WriteLine("iは{0}だ", i); } } class interface05 { public static void Main() { IMas im; //インターフェース①「ます」を型とする変数① IDa id; //インターフェース②「だ」を型とする変数② MyClass mc = new MyClass(); im = mc; //変数①に代入すると im.show(5); //インターフェース①のオーバーライドメソッドが呼ばれ「ます」になる id = mc; //変数②に代入すると id.show(5); //インターフェース②のオーバーライドメソッドが呼ばれ「だ」になる } } p.266 インターフェイスの継承 ・クラスと同様にインターフェースの継承が可能で、基本インターフェース、派生インターフェースとなる ・書式: interface 派生インターフェイス名 : 基本インターフェイス名 {…} p.266 interface06.cs //p.266 interface06.cs using System; interface IInterface1 { //基本インターフェース void setdatano(int n); //抽象メソッド① void setdata(double data, int i); //抽象メソッド② double calcsum(); //抽象メソッド③ } interface IInterface2 : IInterface1 { //派生インターフェース double calcaverage(); //抽象メソッド④ } class MyClass : IInterface2 { //派生インターフェースを実装するクラス double[] data; bool bOK = false; public void setdatano(int n) { //抽象メソッド①のオーバーライド data = new double[n]; bOK = true; } public void setdata(double d, int i) { //抽象メソッド②のオーバーライド if (!bOK) { Console.WriteLine("配列の準備ができていません"); return; } data[i] = d; } public double calcsum() { //抽象メソッド③のオーバーライド if (!bOK) { Console.WriteLine("配列の準備ができていません"); return -1.0; } double sum = 0.0; for (int i = 0; i < data.Length; i++) sum += data[i]; return sum; } public double calcaverage() { //抽象メソッド④のオーバーライド double sum = calcsum(); return sum / data.Length; } } class interface06 { public static void Main() { MyClass mc = new MyClass(); //派生インターフェースを実装するクラスのインスタンスを生成 int nNo; while (true) { //無限ループ Console.Write("データ数---"); string strno = Console.ReadLine(); nNo = Int32.Parse(strno); mc.setdatano(nNo); for (int i = 0; i < nNo; i++) { Console.Write("data[{0}] = ", i); string strdata = Console.ReadLine(); mc.setdata(double.Parse(strdata), i); } Console.WriteLine("合計 = {0}", mc.calcsum()); Console.WriteLine("平均 = {0}", mc.calcaverage()); Console.WriteLine(); Console.Write("続けますか(Y/N)---"); string yn = Console.ReadLine(); if (yn == "N" || yn == "n") break; } } } p.266 インターフェイスの継承・つづき ・基本インタフェースの抽象メソッドと同じシグニチャの抽象メソッドが派生インタフェースにある場合、名前の隠蔽が起こる ・よって、newキーワードにより名前の隠蔽であることを明示すれば良い p.270 interface07.cs //p.270 interface07.cs using System; interface IMyInterface { //基本インターフェース void show1(); //抽象メソッド① void show2(); //抽象メソッド② } interface IMyInterface2 : IMyInterface { //派生インターフェース new void show1(); //抽象メソッド③=①と同じシグニチャなので名前の隠蔽になる void show3(); //抽象メソッド④ } class MyClass : IMyInterface2 { //派生インターフェースを実装するクラス public void show1() { //抽象メソッド③のオーバーライド Console.WriteLine("show1"); } public void show2() { //抽象メソッド②のオーバーライド Console.WriteLine("show2"); } public void show3() { //抽象メソッド④のオーバーライド Console.WriteLine("show3"); } } class interface07 { public static void Main() { MyClass mc = new MyClass(); mc.show1(); //抽象メソッド③のオーバーライドを呼ぶ mc.show2(); //抽象メソッド②のオーバーライドを呼ぶ mc.show3(); //抽象メソッド④のオーバーライドを呼ぶ } } p.266 インターフェイスの継承・つづき ・基本インタフェースの抽象メソッドと同じシグニチャの抽象メソッドが派生インタフェースにある場合、名前の隠蔽が起こる ・この場合、「インターファイス名.」をつける(明示的実装という)ことで双方をオーバーライドすることもできる p.272 interface08.cs //p.272 interface08.cs using System; interface I1 { //基本インターフェース void show1(); //抽象メソッド① void show2(); //抽象メソッド② } interface I2 : I1 { //派生インターフェース new void show1(); //抽象メソッド③=①と同じシグニチャなので名前の隠蔽になる } class MyClass : I2 { //派生インターフェースを実装するクラス void I1.show1() { //抽象メソッド①のオーバーライド Console.WriteLine("I1.show1"); } void I2.show1() { //抽象メソッド③のオーバーライド Console.WriteLine("I2.show1"); } public void show2() { //抽象メソッド②のオーバーライド Console.WriteLine("show2"); } } class interface08 { public static void Main() { MyClass mc = new MyClass(); I1 i1; //基本インターフェースを型とする変数① I2 i2; //派生インターフェースを型とする変数② mc.show2(); //抽象メソッド②のオーバーライドを呼ぶ i1 = mc; //変数①に代入すると i1.show1(); //インターフェース①のオーバーライドメソッドが呼ばれる i2 = mc; //変数②に代入すると i2.show1(); //抽象メソッド③のオーバーライドメソッドが呼ばれる i2.show2(); //抽象メソッド②のオーバーライドメソッドが呼ばれる } } 以上