講義メモ ・p.192「refとout:outキーワード」の補足後、p.195「メソッドのオーバーロード」に進みます p.192 refとout:outキーワード ・refキーワードの上位版がoutキーワードで、refの「実引数はなんらかの値が代入されている必要がある」という制限がない ※C#のバージョン7以降では、outキーワードの仕様が強化され、outキーワードで指定する実引数は、  事前の定義が不要になり「out 型 実引数名」とすれば良い。  この実引数は呼出し以降にそのまま利用可能。 参考:配列を渡して最大値と最小値を返すメソッド using System; class MyClass { public void ArrayMaxMin(double[] x, out double max, out double min) { //仮引数sは参照渡し max = Double.MinValue; min = Double.MaxValue; //仮に最小値・最大値にしておく foreach (var w in x) { //全要素について繰返す max = (max > w) ? max : w; //最大値を更新 min = (min < w) ? min : w; //最小値を更新 } } } class outkeyword01 { public static void Main() { double[] a = {125.3, 16.25, 52.8}; MyClass mc = new MyClass(); mc.ArrayMaxMin(a, out double maxv, out double minv); Console.WriteLine("max = {0}, min = {1}", maxv, minv); } } 提出フォロー:アレンジ演習:p.194 outkeyword01.cs ・変数cの定義を省き、実引数の指定を「out double c」とできることを確認しよう //アレンジ演習:p.194 outkeyword01.cs using System; class MyClass { public void Square(double x, double y, out double s) { //仮引数sは参照渡し s = x * y; } } class outkeyword01 { public static void Main() { //double a = 125.3, b = 16.25, c; double a = 125.3, b = 16.25; //変数cを削除 MyClass mc = new MyClass(); //cには値を代入していません ⇒ 宣言すらしていません mc.Square(a, b, out double c); //out指定の実引数は事前の宣言が不要 Console.WriteLine("縦{0}m, 横{1}mの長方形の面積は{2}平方メートル", a, b, c); } } p.195 メソッドのオーバーロード ・p.168のとおり、シグニチャ(引数の数、型、並び)が異なるコンストラクタを複数定義できることを(コンストラクタの)オーバーロードという ・メソッドもオーバーロードが可能で、シグニチャが異なる同じ名前のメソッドを複数定義できる ・なお、メソッドのシグニチャには戻り値型は含まれないので、戻り値型のみが異なるメソッドの複数定義はできない ・これは呼び出し時に区別できないから ・なお、整数⇒実数のように暗黙の型変換により一致するオーバーロードがあれば、それが採用される ・メソッドのオーバーロードにより、同じ意味のメソッドは同じ名前にできる アレンジ演習:p.195 overload01.cs ・第5のバージョンとして「Method(double x, double y)」を適当な内容で追加し、呼び出せることを確認しよう ・Mainメソッドで、m.Method(5.1, 6)を呼び出すと、暗黙の型変換によりこの第5のバージョンが動作することを確認しよう 作成例 //アレンジ演習:p.195 overload01.cs using System; class manymethods { public int Method(int x) { Console.WriteLine("第1のバージョンが呼ばれました"); return x + 10; } public double Method(double x) { Console.WriteLine("第2のバージョンが呼ばれました"); return x * 2; } public string Method(string x) { Console.WriteLine("第3のバージョンが呼ばれました"); return x += "です"; } public int Method(int x, int y) { Console.WriteLine("第4のバージョンか呼ばれました"); return x + y; } public double Method(double x, double y) { //【以下追加】 Console.WriteLine("第5のバージョンか呼ばれました"); return x + y; } } class overload01 { public static void Main() { manymethods m = new manymethods(); Console.WriteLine("その戻り値は「{0}」です", m.Method(3)); //intなので① Console.WriteLine("その戻り値は「{0}」です", m.Method(3.2)); //doubleなので② Console.WriteLine("その戻り値は「{0}」です", m.Method("粂井")); //stringなので③ Console.WriteLine("その戻り値は「{0}」です", m.Method(5, 6)); //int,intなので④ Console.WriteLine("その戻り値は「{0}」です", m.Method(5.1, 6)); //【追加】double,intだが⑤ } } p.197 Mainメソッドのオーバーロード ・Mainメソッドは特殊なメソッドであり、コンソールプログラム等の動作の始点になる ・通常、public static voidとし、引数は指定しないが、バリエーション(オーバーロード)がある。 ・戻り値型をvoidからintにすると「return 整数値」が記述可能になり、このプログラムを呼び出した元に整数値を返すことができる ・なお、VisualStudioのデバッグ機能から呼び出した場合は、返される値は表示されないが、一般に、正常終了したら0を、でなければ0以外を返すことが多い ・Windowsではビルド済のプログラムをVisualStudioを介さずに実行し、戻り値を表示する方法がある(後述) ・また、引数にstring[]=文字列配列を指定すると、起動時に、システムから複数の文字列を渡すことができる ・この文字列をコマンドライン引数という ・VisualStudioでは、デバッグのプロパティでコマンドライン引数を指定できる ・Windowsではビルド済のプログラムをVisualStudioを介さずに実行し、実行時にコマンドライン引数を指定する方法がある(後述) p.198 main01.cs:VisualStudioにおける実行方法 ① ソースを記述する ②「デバッグ」「●のデバッグプロパティ」 ③「コマンドライン引数」に「cat dog apple」と入力 ④「ファイル」「すべて保存」 ⑤「デバッグなしで開始」 アレンジ演習:p.198 main01.cs ・変数nの宣言と代入を初期化にまとめよう ・forは前判定繰返しなので、nが0の場合、繰返し内容は1度も実行されない。よって、if文は不要なことを確認しよう 作成例 //アレンジ演習:p.198 main01.cs using System; class main01 { public static int Main(string[] s) { //コマンドライン引数を受け取り、整数値を返すMain int n = s.Length; //【変更】コマンドライン引数の数を得る Console.WriteLine("引数の個数は{0}個です", n); for (int i = 0; i < n; i++) { //全要素について繰返す Console.WriteLine("引数{0} : {1}", i + 1, s[i]); } return 0; } } p.198 main01.cs:開発者用コマンドプロンプトにおける実行方法 ① ソースを記述し「ビルド」「ソリューションのビルド」(※既に実行済なら不用) ②「ツール」「コマンドライン」「開発者用コマンドプロンプト」 ③ エクスプローラで「プロジェクト名.exe」ファイルがあるフォルダを探す  例:\ha251_AkibaC#\chap8\chap8\bin\Debug\chap8.exe ④「プロジェクト名.exe」ファイルをエクスプローラから開発者用コマンドプロンプトへドラッグ&ドロップ ⑤ パス名を含むファイル名がペーストされるので、空白を挟みつつ、コマンドライン引数を入力  例:D:\ha251_AkibaC#\chap8>D:\ha251_AkibaC#\chap8\chap8\bin\Debug\chap8.exe boo hoo woo ⑥ Enterキーで実行 ⑦ 実行後「echo %errorlevel%」と入力すると、returnされた値が表示される アレンジ演習:p.198 main02.cs ・returnを実行すると後続の処理は行われないので、elseブロックが不要なことを確認しよう ・実行しても何も表示されないので、確認用に各returnの前で返した値を表示しよう ・わかりづらく実行環境に依存するバッチファイルを使わずに、開発者用コマンドプロンプトを用いて試そう 作成例 //アレンジ演習:p.198 main02.cs using System; class main02 { public static int Main(string[] args) { //コマンドライン引数を受け取り、整数値を返すMain if (args.Length != 1) { Console.WriteLine("return -1"); //【追加】 return -1; } if (!Char.IsDigit(args[0][0])) { //【変更】 Console.WriteLine("return -2"); //【追加】 return -2; } Console.WriteLine("return {0}", int.Parse(args[0])); //【追加】 return int.Parse(args[0]); } } p.201 引数が可変個のメソッド ・メソッドの引数にparamsキーワードと配列を指定することで、引数が可変個のメソッドになる ・よって、同型の引数をいくつでも指定できる1メソッドが記述できる ・書式: アクセス修飾子 戻り値型 メソッド名(params 配列型[] 仮引数) {…} ・例: public void dispall(params int[] p) {…} ・メソッド内部では仮引数を配列として、各引数はその要素になる ・よって、引数の数は、仮引数.Lengthで得ると良い ・引数が0個でもかまわないが、要素数0の配列になるので、要素を使用すると異常終了する 例: public void dispall(params int[] p) { foreach(var w in p) { //仮引数の配列の全要素について繰返す Console.WriteLine(w); } } アレンジ演習:p.201 params01.cs ・forは前判定繰返しなので、animal.lengthが0の場合、繰返し内容は1度も実行されない。よって、if文は不要なことを確認しよう ・for文をforeach文にしよう ・引数に数値を含むとエラーになることを確認しよう 作成例 //アレンジ演習:p.201 params01.cs using System; class MyClass { public void show(params string[] animal) { //可変個引数 //if (animal.Length == 0) //【削除】 // return; //【削除】 foreach (var w in animal) { Console.WriteLine("{0}さんがいます", w); } } public void show(string animal) { // foreach (var w in animal) { Console.WriteLine("{0}さがいます", w); } } } class params01 { public static void Main() { int[] x = {0,1,2}; MyClass mc = new MyClass(); mc.show(); mc.show("きりん", "ぞう", "かば"); //mc.show("きりん", "ぞう", "かば", 1); //文字列に暗黙変換ができない引数があるとエラー } } 補足:可変個引数と通常の引数の共存 ・可変個引数と通常の引数は共存できる。ただし、可変個引数を末尾にすること ・よって、可変個引数は1つしか指定できない  例:public void show(int i, params string[] animal) {…}  こうすると、mc.show(0)、mc.show(0, "hoo")、mc.show(0, "hoo", "voo"); などの呼び出しが可能。  ただし、mc.show()、mc.show("hoo")などの呼び出しはエラーになる。 補足:可変個引数とオーバーロード ・呼び出しにおいて区別できる限り、可変個引数を用いたメソッドのオーバーロードは可能 ・ただし、引数の与え方により、呼び出しが区別できない場合は、呼び出し側がエラーになる  例:void all(params int[] a) {…}、void all(params string[] a) {…} の場合:  こうすると、all(0)、all(0, 1)、all("hoo", "voo"); などの呼び出しが可能。  ただし、all()、mc.show(0, "hoo")などの呼び出しはエラーになる。 ・また、可変個引数を用いたメソッドと可変個引数を用いないメソッドのオーバーロードも可能 ・ただし、引数の与え方により、呼び出しが区別できない場合は、用いないメソッドが優先される ミニ演習:mini0810a.cs ・可変個引数を用いたメソッドと可変個引数を用いないメソッドのオーバーロードで、引数の与え方により、呼び出しが区別できない場合は、用いないメソッドが優先されることを試そう 作成例 //ミニ演習:mini0810a.cs using System; class MyClass { public void show(params string[] animal) { //可変個引数なメソッド① foreach (var w in animal) { Console.WriteLine("{0}さんがいます", w); } } public void show(string animal) { //可変個引数ではないメソッド② Console.WriteLine("{0}がいます", animal); } } class params01 { public static void Main() { int[] x = {0,1,2}; MyClass mc = new MyClass(); mc.show("ひと"); //①②の両方が当てはまるが②が実行される mc.show("きりん", "ぞう", "かば"); //①が実行される } } p.202 静的メンバ ・通常、クラスの各メンバはインスタンスに所属する  例:Slimeクラスにhpを定義すると、Slimeクラスのインスタンスslalinに所属し、slalin.hpになる ・しかし、インスタンスに所属する必要がないような=クラスに所属するメンバも記述できる ・それが静的メンバで、staticを前置して示す  例:Slimeクラスに「生成したスライム数」を持たせる場合、インスタンスには所属しないので、クラスで持つ方が良い ・また、インスタンス変数を全く用いないメソッドもクラスで持つ方が良く、これを静的メソッドという ・静的メンバはクラスに所属するので「クラス名.」を前に付けて実行する ・よって、静的メンバはインスタンスの生成をしなくても利用できる ・なお、Console.WriteLineは、Consoleクラスの静的メソッドなので「Console.」を前につけている ・また、Mathクラスのメンバの大半は静的メンバ(例:Math.PI) ・近い理由で、Mainメソッドも静的メソッドである必要があるので「static」を前置する ・逆に言えば、静的メンバはインスタンスごとにはならない特殊なものであり、オブジェクト指向のメリットを生かせないので乱用は避けること。 ※ つまり、設計図に直接データを書き込んでいるようなイメージ アレンジ演習:p.203 static01.cs ・デフォルトコンストラクタを追加し、その中で静的メンバ変数xをインクリメントしよう ・Mainメソッドで静的メンバ変数xの最初の値を10ではなく0にしよう ・そして、MyClassのインスタンスを生成してから、ShowX()メソッドを実行することを繰返してみよう 作成例 // static01.cs using System; class MyClass { public static int x; //静的データメンバ public static void showX() { //静的メソッド Console.WriteLine("x = {0}", x); //静的データメンバのみを使う } public MyClass() { //【以下追加】デフォルトコンストラクタ x++; //静的データメンバをインクリメント } } class static01 { public static void Main() { //実はMainも静的メソッド MyClass.x = 0; //【変更】静的データメンバなので「クラス名.」で利用可能 MyClass.showX(); //静的メソッド「クラス名.」で実行可能 new MyClass(); //【以下追加】デフォルトコンストラクタを呼ぶ MyClass.showX(); //1になっている new MyClass(); //デフォルトコンストラクタを呼ぶ MyClass.showX(); //2になっている } } p.204 静的クラス ・静的メンバのみによるクラスは、静的クラスにすることができる ・書式: static class クラス名 {…} ・義務はないが、静的クラスにしておくことで、誤って静的ではないメンバが出現することを防止できる  ※ 例えば、固定データのみを持つクラスや、数値関数のみを持つクラスなど(特殊な事例) アレンジ演習:p.204 static02.cs ・静的クラスにインスタンスメンバを追記してエラーになることを確認しよう 作成例 //アレンジ演習:p.204 static02.cs using System; static class MyClass { //静的クラス public static int x; //静的データメンバ // int y; //インスタンス変数を記述するとエラー public static void showX() { //静的メソッド System.Console.WriteLine("x = {0}", x); } // void foo() { } //インスタンスメソッドを記述するとエラー } class static02 { public static void Main() { MyClass.x = 10; //静的データメンバを用いる MyClass.showX(); //静的メソッドを実行 } } p.205 静的メンバとインスタンスメンバの混在 ・静的クラスでなければ、静的メンバとインスタンスメンバの混在が可能だが、下記のルールがある ① 静的メソッドはインスタンス変数を扱えない(区別できないので) ② 静的メソッドはインスタンスメソッドを呼べない(〃) ③ 静的メソッドはthisを使えない(thisはインスタンスを指すので) ・なお、インスタンスメンバから静的メンバへのアクセスには制限はない(クラスに1つしかないので共用できる) p.205 static03.cs 解説図  https://ha241.rundog.org/wp-content/uploads/2024/08/DSC_9304.jpg アレンジ演習:p.205 static03.cs ・静的メソッドSetCatTail()の中の「// name = "マイケル";」をコメントアウトしないと、どういうエラーになるか確認しよう ・静的メソッドSetCatTail()の中で、インスタンスメソッドShowCat()を呼ぶとエラーになることを確認しよう ・静的メソッドSetCatTail()の中で、thisを用いるとエラーになることを確認しよう ・以上は、確認後、コメントアウトすること 提出:アレンジ演習:p.205 static03.cs 次回(8/24)予告:p.207「プロパティ」から