講義メモ ・p.176「既存のクラスを使ってみる」から 補足:thisの用途(コンストラクタからコンストラクタを呼ぶ) ・コンストラクタに「: this(引数,…)」を記述することで、他のコンストラクタに引数を渡して呼び出すことができる ・例えば、public クラス名(int a, int b) {…} があれば、  public クラス名(int a) : this(a, 100) {} とすることで、上のコンストラクタを呼び出せる  public クラス名() : this(200) {} とすることで、上のコンストラクタを呼び出せる ・この場合、コンストラクタの本文は空出も構わないが「{}」は省略不可 ・なお、近い構文に「: base(引数,…)」があり、継承時のコンストラクタの扱いに用いる(p.245) 利用例:アレンジ演習: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(); //整数から文字列に変換して代入 } Console.WriteLine("氏名:{0} 住所:{1} 年齢:{2}", name, address, toshi); } public MyClass(string str1, string str2, int x) { //コンストラクタ③(string,string,int) 全て name = str1; //氏名は引数1から address = str2; //住所は引数2から age = x; //年齢は引数3から } public MyClass(string str) : this(str, -1) { }//コンストラクタ①(string) 氏名のみ⇒④を呼ぶ public MyClass(int x) : this("不明", "不定", x) { } //コンストラクタ②(int) 年齢のみ⇒③を呼ぶ public MyClass(string str, int x) : this(str, "不定", x) { } //コンストラクタ④(string,int) 氏名と年齢⇒③を呼ぶ public MyClass() : this(-1) { } //デフォルトコンストラクタ⇒②を呼ぶ } class construct01 { public static void Main() { MyClass mc1 = new MyClass(18); //コンストラクタ②(int) 年齢のみ を呼ぶ MyClass mc2 = new MyClass("粂井康孝"); //コンストラクタ①(string) 氏名のみ を呼ぶ MyClass mc3 = new MyClass("田中太郎", "東京都", 32); //コンストラクタ③(全部)を呼ぶ mc1.Show(); mc2.Show(); mc3.Show(); MyClass mc4 = new MyClass("シャア", 20); //【追加】コンストラクタ④(string,int)を呼ぶ mc4.Show(); //【追加】 MyClass mc5 = new MyClass(); //【追加】デフォルトコンストラクタ()を呼ぶ mc5.Show(); //【追加】 } } p.176 既存のクラスを使ってみる ・すでに用いている「Console.Write(…)」メソッドなどの「Console」や、「Math.Sin(…)」メソッドなどの「Math」などはC#が提供するクラス ・C#が提供するクラスの中には効率的なデータ構造を提供するコレクションクラスが含まれている ・代表的なコレクションクラスがArrayListクラスで、自由度の高い配列機能を提供する ・ArrayListオブジェクトでは、要素数の変更や、その時点の要素数を得る手段などを利用できる  ※テキスト中にある「プロパティ」についは8章で後述するが、引数のない簡易的なメソッド。 ・ArrayListオブジェクトに格納した要素は、配列要素と同様に添字で直接扱うことも可能 ・利用には「using System.Collections;」を用いると良い ・Countプロパティ(要素数を得る手段)とfor文を用いることで、全要素に対する処理を記述できるが、閲覧用であればforeachを用いることも可能 アレンジ演習:p.177 ArrayList01.cs ・forの代わりにforeachを用いよう ・終了フラグbEndを用いないようにしよう 作成例 //アレンジ演習: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(); //配列機能を持つアレイリストクラスのオブジェクトを生成 while (true) { //無限ループ Console.Write("データ(数値以外入力で終了)-- "); strData = Console.ReadLine(); if (!Char.IsDigit(strData[0]) && strData[0] != '-') { //先頭文字が数字ではなく'-'でもない //bEnd = true; //【削除】終了フラグをオン break; //【移動】 } else { al.Add(double.Parse(strData)); //入力文字列を実数変換してアレイリストに格納 } } int n = 0; //【追加】添字の代わり foreach (var w in al) { //【変更】全要素について繰り返す Console.WriteLine("Data[{0}] = {1}", n++, w); sum += (double)w; //実数に逆変換して合計に足し込む } int count = al.Count; //Countで要素数を得る double avr = sum / count; //合計を要素数で割って平均を得る Console.WriteLine("データ個数 = {0}", count); Console.WriteLine("平均値 = {0}", avr); } } p.180 練習問題1 ex0701.cs 指針とヒント ・変数名、クラス名などは自由 ・Mainメソッドはこのクラスとは別のクラスに配置すると良い ・よって、インスタンスの生成が必要 作成例 //p.180 練習問題1 using System; class MyClass { public int age; } class arraylist01 { public static void Main() { MyClass m = new MyClass(); //オブジェクトを生成 m.age = 63; Console.WriteLine("m.age = {0}", m.age); } } p.180 練習問題2 ヒント ・メソッド名、クラス名などは自由 ・テキスト中ではコンストラクタのオーバーロードしか説明していないが、講義メモの通り、メソッドもオーバーロードが可能 ・int型2値の和を求めるメソッドの戻り値型はint、double型2値の和を求めるメソッドの戻り値型はdoubleとすれば良い 作成例 //p.180 練習問題2 using System; class MyClass { public int Add(int a, int b) { //整数和を返すメソッド① return a + b; } public double Add(double a, double b) { //実数和を返すメソッド② return a + b; } } class ex0702 { public static void Main() { MyClass m = new MyClass(); //オブジェクトを生成 Console.WriteLine("m.Add(1, 2) = {0}", m.Add(1, 2)); //(int,int)なので①を呼ぶ Console.WriteLine("m.Add(1.1, 2.2) = {0}", m.Add(1.1, 2.2)); //(double,double)なので②を呼ぶ } } 第8章 クラスとメソッドの詳細 p.181 メソッドの再帰呼び出し ・C#などのメソッドは内部で自分自身を呼び出すことが可能で、これを再帰(リカージョン)という ・再帰を用いることで、アルゴリズムを単純化できることがある ・なお、再帰を用いる場合は「場合によっては自分自身を呼び出さず、値を返す」処理が必要で、  これがないと無限ループする  ※「再帰の終了条件」ともいう p.181 階乗を計算する ・階乗とはある正の整数において、その数から1までの全整数の積のことで「整数!」と表す ・例: 3! = 3×2×1 = 6、4! = 4×3×2×1 = 24 ・階乗は繰返し構文で計算可能だが、n! = n × (n - 1)! であることを用いて再帰でシンプルに記述できる ・例: 3! = 3×2×1 = 6、4! = 4×(3×2×1) = 4×3! ・なお、0の階乗及び1の階乗は1と定められているので、p.182 fact01.csでは0の階乗が1であることを  再帰の終了条件にしている アレンジ演習:p.182 fact01.cs ・0の階乗が1であることを再帰の終了条件にしているが、1の階乗も1であることを加味して効率化しよう ・条件演算子を用いてCalcFactメソッドを簡略化しよう 作成例 //アレンジ演習:p.182 fact01.cs using System; class Fact { public long CalcFact(int n) { //n!を返す再帰を含むメソッド return (n == 0 || n == 1) ? 1 : n * CalcFact(n - 1); //0!=1!=1、n!=n*(n-1)! } } class fact01 { public static void Main() { Fact f = new Fact(); for (int i = 0; i <= 20; i++) { Console.WriteLine("{0}! = {1}", i, f.CalcFact(i)); } } } p.185 フィボナッチ数列を求める ・フィボナッチ数列とは先頭2要素の値を1とし、3要素目以降は前2要素の和を値とする数列 ・例: 1, 1, 2, 3, 5, 8, 13, 21, 34,… ・各種のシミュレーション、統計処理に利用されている ・これは、先頭を1要素目とすると、3要素目は1要素目+2要素目なので、n要素目は  「(n-2)要素目+(n-1)要素目」  という再帰表現にできる ・なお、再帰の終了条件は「1要素目と2要素目は1」を用いると良い アレンジ演習:p.185 fibonacci.cs ・条件演算子を用いてメソッドを簡略化しよう 作成例 //アレンジ演習:p.185 fibonacci.cs using System; class fibo { public long CalcFibo(int n) { //フィボナッチ数列のn番目の値を返す(先頭を1番目とする) return (n == 1 || n == 2) ? 1 : CalcFibo(n - 1) + CalcFibo(n - 2); //再帰を含む } } class fibonacci { public static void Main() { fibo f = new fibo(); for (int i = 1; i <= 30; i++) { Console.WriteLine("f({0}) = {1}", i, f.CalcFibo(i)); } } } p.188 refとout:前提の説明「値渡しについて」 ・メソッドの仮引数には、実引数で指定された値や式の評価の値が渡される ・このことを値渡しという ・p.189 swap01.csは、Swapメソッドの中で仮引数の値を書き換えており、実引数の値が書き換わると考えたプログラム ・実引数と仮引数が同じ名前なので誤解を招きやすいが、コピーされた値が用いられるので、実引数の値は変化しない p.190 refとout:前提の説明「参照型における値渡しについて」 ・他の言語では参照型の引数を用いると、値渡しにならず、実引数に参照型変数を指定し、仮引数を操作すると実引数が書き換わるものもあるが、C#では値渡しになる ・よって、p.190 swap02.csは、p.189 swap01.csと同様になり、実引数の値は変化しない p.191 refとout:前提の説明「配列を実引数にすると参照渡しになる」 ・p.191 changearrray01.csの通り、配列を実引数にすると配列の位置情報(参照)が値として渡されるので、仮引数も同じ配列を指すことになる ・よって、配列を実引数にすると値渡しでなく、参照渡しと呼ばれる受け渡しになる ・よって、仮引数である配列の要素値を操作すると、実引数である配列の要素値が変化する ・もちろん、仮引数である配列と実引数である配列の配列名は同じである必要はない アレンジ演習:p.191 changearrray01.cs ・「new int[3]」を省略して、配列の末尾に要素「4」を追加しよう ・すると、プログラムの他の部分をまったく変更しなくてよいことを確認しよう //アレンジ演習:p.191 changearrray01.cs using System; class change { public void modify(int[] array) { //配列を受け取るメソッド(参照渡しになる) int n = array.Length; //受け取った配列の要素数を得る for (int i = 0; i < n; i++) { //全要素について繰返す array[i] *= 2; //要素値を2倍にする(参照渡しなので呼び出し側で指定した配列が書き換わる) } } } class changearray01 { public static void Main(){ change c = new change(); int[] myarray = {1, 2, 3, 4}; //【変更】 Console.WriteLine("----modifyメソッド実行前----"); int i = 0; foreach (int x in myarray) { //全要素について繰返す Console.WriteLine("myarray[{0}] = {1}", i, x); i++; } c.modify(myarray); //配列をメソッドに渡して書き換えてもらう Console.WriteLine("----modifyメソッド実行後----"); i = 0; foreach (int x in myarray) { //全要素について繰返す Console.WriteLine("myarray[{0}] = {1}", i, x); i++; } } } テキスト正誤 p.191 changearrray01.cs 1行目 【誤】// charngearrray01.cs 【正】// changearrray01.cs ミニ演習:p.191 changearrray01.cs ⇒ changearrraylist.cs ・changearrray01.csの配列をArrayListオブジェクトに変更して、動作を確認しよう 作成例 //ミニ演習:p.191 changearrray01.cs ⇒ changearrraylist.cs using System; using System.Collections; //【追加】 class change { public void modify(ArrayList array) { //【変更】アレイリストを受け取るメソッド(参照渡しになる) int n = array.Count; //【変更】受け取ったアレイリストの要素数を得る for (int i = 0; i < n; i++) { //全要素について繰返す int w = (int)array[i] * 2; //【変更】要素値を取り出して2倍にする array[i] = w; //【追加】再格納する } } } class changearray01 { public static void Main(){ change c = new change(); ArrayList myarray = new ArrayList(); //【追加】アレイリストのオブジェクトを生成 for (int n = 1; n <= 4; n++) { //【以下追加】 myarray.Add(n); //アレイリストに格納 } Console.WriteLine("----modifyメソッド実行前----"); int i = 0; foreach (int x in myarray) { //全要素について繰返す Console.WriteLine("myarray[{0}] = {1}", i, x); i++; } c.modify(myarray); //アレイリストをメソッドに渡して書き換えてもらう i = 0; foreach (int x in myarray) { //全要素について繰返す Console.WriteLine("myarray[{0}] = {1}", i, x); i++; } } } p.192 refとout:refキーワード ・ここまでの説明の通り(配列のような構造を渡す場合を除いて)引数においては値渡しが基本であり、メソッド側で仮引数の値を操作しても、実引数には反映しない。 ・これを参照渡しに切り替えるのがrefキーワード ・実引数と仮引数の双方にrefキーワードを与えると、メソッド側で仮引数の値を操作すると実引数には反映する。 ・実引数名と仮引数名が異なっても構わない。 ・なお、refキーワードを付けて用いる実引数はなんらかの値が代入されている必要がある ・また、呼び出し側で式やリテラルを実引数として与えると文法エラーになる アレンジ演習:p.193 swap03.cs ・「refキーワードを付けて用いる実引数はなんらかの値が代入されている必要がある」ことを試そう ・「呼び出し側で式やリテラルを実引数として与えると文法エラーになる」ことを試そう ・どちらも、確認したらコメントアウトすること 作成例 //アレンジ演習:p.193 swap03.cs using System; class myclass { private int temp; public void swap(ref int x, ref int y) { //仮引数xとyは参照渡しになる temp = x; x = y; //この時点で仮引数xが参照している実引数に代入 y = temp; //この時点で仮引数yが参照している実引数に代入 } } class swap01 { public static void Main() { myclass s = new myclass(); int x = 10, y = 20; s.swap(ref x, ref y); //実引数xとyは参照渡しになる(値が書き換わる) Console.WriteLine("x = {0}, y = {1}", x, y); //値が交換されている //int z; //s.swap(ref x, ref z); //「未割当のローカル変数zが使用された」という文法エラーになる //s.swap(ref x, ref(x + y)); //「割当て可能な変数でなければならない」という文法エラーになる //s.swap(ref x, ref(200)); //「割当て可能な変数でなければならない」という文法エラーになる } } p.192 refとout:outキーワード ・refキーワードの上位版がoutキーワードで、refの「実引数はなんらかの値が代入されている必要がある」という制限がない ※C#のバージョン7以降では、outキーワードの仕様が強化され、outキーワードで指定する実引数は、事前の定義が不要になり「out 型 実引数名」とすれば良い。  この実引数は呼出し以降にそのまま利用可能。 アレンジ演習:p.194 outkeyword01.cs ・変数cの定義を省き、実引数の指定を「out double c」とできることを確認しよう 提出:アレンジ演習:p.193 swap03.cs、または、アレンジ演習:p.194 outkeyword01.cs 次回予告:p.192「refとout:outキーワード」の補足後、p.195「メソッドのオーバーロード」に進みます