講義メモ ・「p.168 コンストラクタ:コンストラクタのオーバーロード」から p.167 コンストラクタ:コンストラクタの引数(再掲載) ・メソッドと同様にコンストラクタにも引数が指定できる ・引数を指定したコンストラクタを呼び出すには、newにおいてカッコ内に引数型と引数を記述する ・定義書式②: public クラス名(引数型 引数名, …) {処理内容}  例: public Slime(int h, int p) { hp = h; mp = p; } ・呼び出し法: new クラス名(値や式, …); p.168 コンストラクタ:コンストラクタのオーバーロード ・コンストラクタを含むメソッドは、引数で呼び出し方を区別できるのであれば、同じ名前で定義しても良い ※ C++の関数も同様だが、C言語などの関数では不可 ・「呼び出し方を区別できる」とは、引数の数や引数型や順番の違いのこと ・この「引数の数や引数型や順番の違う」コンストラクタのことをコンストラクタのオーバーロードという ・例: public Monster(){…} //①引数のないコンストラクタ ・例: public Monster(int hp){…} //②HPのみを引数で渡すコンストラクタ ・例: public Monster(int hp, int mp){…} //③HPとMPを引数で渡すコンストラクタ ・例: public Monster(string name){…} //④nameのみを引数で渡すコンストラクタ ・上記は呼び出し方によって自動的に使い分けられる ・例: Monster Slalin = new Monster(); //①が呼ばれる ・例: Monster Hoimin = new Monster(20); //②が呼ばれ、20はHPに用いられる ・例: Monster Rimuru = new Monster(100, 200); //③が呼ばれ、100はHPに、200はMPに用いられる ・例: Monster Veldra = new Monster("ヴェルドラ"); //④が呼ばれ、"ヴェルドラ"は名前に用いられる ・メソッドのオーバーロードの場合、名前と引数の数や引数型や順番の違いで判別する ・この「名前が同じで、引数の数や引数型や順番の違う」メソッドのことをメソッドのオーバーロードという ・例: public void add(){…} //①引数のないメソッド ・例: public void add(int hp){…} //②HPのみを引数で渡すメソッド ・例: public void add(int hp, int mp){…} //③HPとMPを引数で渡すメソッド ・上記は呼び出し方によって自動的に使い分けられる ・例: add(); //①が呼ばれる ・例: add(20); //②が呼ばれ、20はHPに用いられる ・例: add(100, 200); //③が呼ばれ、100はHPに、200はMPに用いられる ・なお、メソッド名と、引数の数や引数型や順番の情報を合わせて、メソッドのシグニチャという ・コンストラクタは戻り値がないが、メソッドの戻り値型はオーバーロードのシグニチャに含まれない ・戻り値型のみが異なる場合、呼び出し方によって自動的に使い分けることはできないので、エラーになる ・例: public int add(int hp, int mp){…} //④エラー(③と同じシグニチャなので) アレンジ演習:p.169 construct02.cs ・名前と年齢を渡すコンストラクタのオーバーロードを追加しよう ・これを呼び出す処理を追記しよう 作成例 //p.169 construct02.cs using System; class MyClass { private string name; //フィールド private int age; private string address; public void Show() { //メソッド string toshi; if (age == -1) { toshi = "不明"; } else { toshi = age.ToString(); //整数を文字列に変換するメソッド(Int32クラス) } Console.WriteLine("氏名:{0} 住所:{1} 年齢:{2}", name, address, toshi); } public MyClass(string str) { //コンストラクタ①(文字列) name = str; address = "不定"; age = -1; } public MyClass(int x) { //コンストラクタ②(整数) age = x; name = "不明"; address = "不定"; } public MyClass(string str1, string str2, int x) { //コンストラクタ③(文字列,文字列,整数列) name = str1; address = str2; age = x; } public MyClass(string str1, int x) { //【以下追加】コンストラクタ④(文字列,整数列) name = str1; address = "不定"; age = x; } } class construct01 { //実行用クラス public static void Main() { MyClass mc1 = new MyClass(18); //コンストラクタ②(整数)が呼ばれる MyClass mc2 = new MyClass("粂井康孝"); //コンストラクタ①(文字列)が呼ばれる MyClass mc3 = new MyClass("田中太郎", "東京都", 32); //コンストラクタ③(文字列,文字列,整数列)が呼ばれる mc1.Show(); mc2.Show(); mc3.Show(); MyClass mc4 = new MyClass("ヴェルファイア", 200); //【以下追加】コンストラクタ④(文字列,整数列)が呼ばれる mc4.Show(); } } p.168 コンストラクタ:コンストラクタのオーバーロードとデフォルトコンストラクタ ・通常、デフォルトコンストラクタは自動的に用意される ・自前で記述することもできる ・なお、コンストラクタのオーバーロードがされている場合、デフォルトコンストラクタは自動的に用意されない ・よって、自前で記述する必要がある アレンジ演習:p.169 construct02.cs・つづき ・Mainメソッドに、引数のないインスタンス生成を追記し、どうなるか確認しよう ・必要であれば、デフォルトコンストラクタを自前で記述し、動作を確認しよう 作成例 //アレンジ演習:p.169 construct02.cs using System; class MyClass { private string name; //フィールド private int age; private string address; public void Show() { //メソッド string toshi; if (age == -1) { toshi = "不明"; } else { toshi = age.ToString(); //整数を文字列に変換するメソッド(Int32クラス) } Console.WriteLine("氏名:{0} 住所:{1} 年齢:{2}", name, address, toshi); } public MyClass(string str) { //コンストラクタ①(文字列) name = str; address = "不定"; age = -1; } public MyClass(int x) { //コンストラクタ②(整数) age = x; name = "不明"; address = "不定"; } public MyClass(string str1, string str2, int x) { //コンストラクタ③(文字列,文字列,整数列) name = str1; address = str2; age = x; } public MyClass(string str1, int x) { //コンストラクタ④(文字列,整数列) name = str1; address = "不定"; age = x; } public MyClass() { //デフォルトコンストラクタ()の代わりのコンストラクタ⑤ name = "不明"; address = "不定"; age = -1; } } class construct01 { //実行用クラス public static void Main() { MyClass mc1 = new MyClass(18); //コンストラクタ②(整数)が呼ばれる MyClass mc2 = new MyClass("粂井康孝"); //コンストラクタ①(文字列)が呼ばれる MyClass mc3 = new MyClass("田中太郎", "東京都", 32); //コンストラクタ③(文字列,文字列,整数列)が呼ばれる mc1.Show(); mc2.Show(); mc3.Show(); MyClass mc4 = new MyClass("ヴェルファイア", 200); //コンストラクタ④(文字列,整数列)が呼ばれる mc4.Show(); MyClass mc5 = new MyClass(); //【以下追加】デフォルトコンストラクタの代わり⑤が呼ばれる mc5.Show(); } } p.171 デストラクタ(ガベージコレクション) ・プログラムの中で生成されたオブジェクト(インスタンス)は、基本的に、プログラムの終了時に破棄される ・C#では、プログラマが意図的にオブジェクト(インスタンス)を削除(無効化)しても、即時には処理されない ・C#などではガベージコレクション機能があり、システム側で適時にオブジェクトの掃除を行うことで、メモリの利用効率を高めている ・なお、プログラム内でガベージコレクション機能を実行させることはできない ・参照されなくなったオブジェクトはガベージコレクションの対象になるので、参照を空にしたり、他のオブジェクトを指すようにすることで、オブジェクトを実質的に消すことは可能。 ・例: Monster m = new Monster(); Monster n = new Monster(); m = n; //最初のnewで生成したオブジェクトmは消される p.171 デストラクタ(デストラクタ) ・オブジェクトの生成時に呼ばれるコンストラクタに対して、オブジェクトの消去時に呼ばれる特殊なメソッドがデストラクタ ・デストラクタを直接呼び出したり、動作タイミングを成業することはできない(ガベージコレクションと同様) ・デストラクタは内容が空のものが自動的に用意されるが自前で記述することもできる ・コンストラクタが準備作業を行うことに対して、デストラクタは後始末に用いることができる ・定義書式: ~クラス名(){…} ・コンストラクタとは異なり、引数は指定できない(よってオーバーロードも不可) ・戻り値はなく、voidを指定することもできない ・複数のオブジェクトが消された場合、デストラクタの実行順序は不定だが確実に実行される アレンジ演習:p.172 destruct01.cs ・変数dt2、dt3を使用せず、変数dt1のみで3つのオブジェクトを順に扱うようにしたらどうなるか確認しよう  ⇒現状と同様(メモリが非常に少なければ、ガベージコレクションが起こり、デストラクタが動作する可能性はある) 作成例 //アレンジ演習:p.172 destruct01.cs using System; class DestructTest { int x; // デストラクタ ~DestructTest() { //戻り値型無し、引数無し Console.WriteLine("デストラクタが呼ばれました"); Console.WriteLine("xは{0}です", x); } // 引数付きコンストラクタ public DestructTest(int n) { //戻り値型無し、引数有 Console.WriteLine("コンストラクタが呼ばれました"); x = n; Console.WriteLine("xに{0}を代入しました", n); } } class destruct { public static void Main() { DestructTest dt1 = new DestructTest(1); //コンストラクタが呼ばれる① Console.WriteLine("dt1生成"); dt1 = new DestructTest(2); //コンストラクタが呼ばれる②(これで①は不要になる) Console.WriteLine("dt1上書①"); dt1 = new DestructTest(3); //コンストラクタが呼ばれる③(これで②も不要になる) Console.WriteLine("dt1上書②"); Console.ReadLine(); //入力待ち } //この直後にデストラクタが3つ呼ばれる } p.174 this ・自分自身を指す参照を与えるキーワードがthis ・(テキストのサンプルは文法説明にしかなっていないが)クラスの中で自分自身を指す参照を操作に利用できる p.174 this01.cs //p.174 this01.cs using System; class MyClass { public MyClass m1, m2; //自クラスを型とするフィールド public void Test() { m2 = this; //自クラス型なので自分への参照を代入できる } public MyClass() { //コンストラクタ m1 = this; //自クラス型なので自分への参照を代入できる } } class this01 { public static void Main() { MyClass mc = new MyClass(); //コンストラクタが呼ばれ、m1にthisが代入される mc.Test(); //メソッドが呼ばれ、m2にthisが代入される if (mc.m1 == mc.m2) Console.WriteLine("m1とm2は同じです"); if (mc == mc.m1) //mcは自オブジェクトなのでm1、m2と同じ Console.WriteLine("mcとm1は同じです"); if (mc == mc.m2) //同上 Console.WriteLine("mcとm2は同じです"); } } 補足:thisの活用法① 自オブジェクトのデータを書き換えた結果を返すメソッド ・thisを使わずに自分のHPを倍にすると、単独で実行するしかない class Monster { public int hp; public void hpx2() { hp = hp * 2; } } : Monster slalin = new Monster(); slalin.hp = 100; slalin.hpx2(); //200になる slalin.hpx2(); //400になる slalin.hpx2(); //800になる Console.WriteLine(slalin.hp); ・thisを使って自分のHPを倍にすると、メソッドの結果をメソッドに渡すことで効率化できる class Monster { public int hp; public Monster hpx2() { hp = hp * 2; return this; } //HPを2倍し、自分への参照を返す } : Monster slalin = new Monster(); slalin.hp = 100; slalin.hpx2().hpx2().hpx2(); //800になる Console.WriteLine(slalin.hp); ・応用例 using System; class Monster { public int hp, mp; public Monster hpx2() { hp = hp * 2; return this; } public Monster mpx2() { mp = mp * 2; return this; } } class this01 { public static void Main() { Monster slalin = new Monster(); slalin.hp = 100; slalin.mp = 150; slalin.hpx2().hpx2().hpx2().mpx2(); //HPは800、MPは300になる Console.WriteLine("{0},{1}", slalin.hp, slalin.mp); } } 補足:thisの活用法② インスタンス変数を用いるメソッドやコンストラクタの引数名を変数名と同じにできる ・下記のように「this.」はインスタンス変数、そうでない方は引数。 class Monster { public int hp, mp; public Monster(int hp) { this.hp = hp; } public void addmp(int mp) { this.mp += mp; } } ※ この記述スタイルの利用をチームルールにしている場合もある p.176 既存のクラスを使ってみる ・C#が提供するArrayListクラスには、コンストラクタやインスタンスメソッド(プロパティ)などがある  https://learn.microsoft.com/ja-jp/dotnet/api/system.collections.arraylist ・ArrayListクラスは必要に応じてサイズが動的に拡大されるArrayList配列を提供してくれる ・利用には、using System.Collections; を指定すると良い ・ArrayList配列の生成は、ArrayListクラスのデフォルトコンストラクタを呼ぶだけで良い(生成時の型指定は不要) ・ArrayList配列への要素の格納は、参照変数.Add(要素) で良く、要素の型は自由 ・ArrayList配列の要素は配列と同様に 参照変数[添字] で直接扱える。 ・ただし、型情報を失っているので、計算などにはキャストしてから扱うこと ・ArrayList配列の要素数は、参照変数.Count で得られる(プロパティ(p.207)) p.177 arraylist01.cs //p.177 arraylist01.cs using System; using System.Collections; class arraylist01 { public static void Main() { bool bEnd = false; //終了フラグをオフにする string strData; //入力用 double sum = 0.0; //合計 ArrayList al = new ArrayList(); //ArrayList配列を生成 while (true) { //無限ループ Console.Write("データ(数値以外入力で終了)-- "); strData = Console.ReadLine(); if (!Char.IsDigit(strData[0]) && strData[0] != '-') { //先頭文字が数字でなく-でもない? bEnd = true; //終了フラグをオンにする } else { al.Add(double.Parse(strData)); //ArrayList配列に格納 } if (bEnd) { //終了フラグがオン? break; //ループを抜ける } } for (int i = 0; i < al.Count; i++) { //ArrayList配列の全データについて繰返す Console.WriteLine("Data[{0}] = {1}", i + 1, al[i]); sum += (double)al[i]; //実数に戻して合計に足し込む } int count = al.Count; //ArrayList配列の要素数を得る double avr = sum / count; //合計を割って平均値を得る Console.WriteLine("データ個数 = {0}", count); Console.WriteLine("平均値 = {0}", avr); } } 提出:アレンジ演習:p.177 arraylist01.cs ・終了フラグを用いないようにしよう ・foreachが利用できるか試してみよう 次回予告:p.180「第7章 練習問題」から