講義メモ ・p.207「プロパティ」から 提出フォロー:アレンジ演習:p.205 static03.cs ・静的メソッドSetCatTail()の中の「// name = "マイケル";」をコメントアウトしないと、どういうエラーになるか確認しよう ・静的メソッドSetCatTail()の中で、インスタンスメソッドShowCat()を呼ぶとエラーになることを確認しよう ・静的メソッドSetCatTail()の中で、thisを用いるとエラーになることを確認しよう ・以上は、確認後、コメントアウトすること 作成例 //アレンジ演習:p.205 static03.cs using System; class Cat { static int NoOfTail; //静的データメンバ(猫の尾の数は猫に所属) string Name; //インスタンス変数(名前はインスタンスに所属) public void SetName(string strName) { //インスタンスメソッド Name = strName; //インスタンス変数を用いるので } public void ShowCat() { //インスタンスメソッド if (Name == null) { //インスタンス変数を用いるので Console.WriteLine("名前が設定されていません"); return; } Console.WriteLine("猫の名前は{0}で尾の数は{1}本です", Name, NoOfTail); //静的データメンバを用いることも可能 } public static void setCatTail(int no) { //静的メソッド //ここでインスタンスフィールドにアクセス不可 // Name = "マイケル"; //インスタンス変数にはアクセスできない NoOfTail = no; //静的データメンバにはアクセスできる // ShowCat(); //インスタンスメソッドは呼び出せない // this.Name = ""; //thisは使えない } } class static03 { public static void Main() { Cat.setCatTail(1); //静的メソッドを呼ぶ Cat mycat = new Cat(); Cat yourcat = new Cat(); mycat.ShowCat(); //インスタンスメソッドを呼ぶ mycat.SetName("マイケル"); //同 yourcat.SetName("パトリシア"); //同 mycat.ShowCat(); //同 yourcat.ShowCat(); //同 } } p.207プロパティ ・メソッドに近い記述により、外部からは変数のように扱える仕掛がプロパティ。  ※ データベースなどでは「属性」のことをプロパティというが、C#のプロパティは意味が異なる ・変数などを扱うプロパティを定義して、外部に利用を許可することで、外部からはプロパティがデータそのものに見える ・プロパティの中でデータの利用を制限したり、更新を不可にしたりできるので、データの安全性を高めることが可能  例: 身長を表す変数に負の数を代入できるが、身長を扱うプロパティの中で負の数の代入を却下できる ・プロパティで扱う変数をprivate指定にし、プロパティをpublicにすると良い ・プロパティは1つのデータを受け渡すので、そのデータ型がプロパティの型になる ・プロパティにはgetとsetの2つのアクセッサ(アクセス記述)があり、どちらかは省略可能 ・外部で、プロパティを利用するとgetの内容が、プロパティに代入するとsetの内容が実行される ・getの基本構文は「get { return 式や値; }」で、returnが必ず実行される必要がある ・setの基本構文は「set { 変数など = value; }」で、代入した値がvalueキーワード経由で渡される  定義例(負の身長を排除できる):  private double height; //身長を直接操作することを禁止  pubic double high { get { return height; } set { height = (value > 0) ? value : 0; } } アレンジ演習:p.208 prop01.cs ・各アクセッサにコンソール出力を追記し、自動的に呼び出されていることを確認しよう 作成例 //アレンジ演習:p.208 prop01.cs using System; class MyClass { double bl; //プロパティで扱う変数(publicがないのでprivate扱い) public double blprop { get { //プロパティを利用すると実行される内容 Console.WriteLine("getを実行"); //【追加】 return bl; //returnが必ず実行される必要がある } set { //プロパティに代入すると実行される内容 Console.WriteLine("setを実行"); //【追加】 bl = value; //代入した値がvalueキーワード経由で渡される } } } class prop01 { public static void Main() { MyClass mc = new MyClass(); mc.blprop = 165.2; //プロパティのsetが実行され、valueに165.2が渡される Console.WriteLine("bl = {0}", mc.blprop); //プロパティのgetが実行され、インスタンス変数blの値が戻る } } p.207 プロパティ:setアクセッサにおける不適切な代入の防止 ・setアクセッサに条件を記述することで、代入できる値や相手を制限できる ・なお、setアクセッサを省略すると読み込み専用に、getアクセッサを省略すると閲覧不可にできる アレンジ演習:p.209 prop02.cs ・setアクセッサを省略した試験的なプロパティを追加し、このプロパティを代入に利用するとエラーになることを確認しよう 作成例 //p.209 prop02.cs using System; class BMI { double bw, bl; //体重、身長(publicがないのでprivate扱い) public double blprop { //身長のプロパティ get { //プロパティを利用すると実行される内容(無制限) return bl; } set { //プロパティに代入すると実行される内容(制限有り) if (value <= 0) { Console.WriteLine("身長に0または負の値は設定できません"); return; //プロパティの途中で呼ばれた相手に戻る } bl = value; //代入した値がvalueキーワード経由で渡される } } public double bwprop { //体重のプロパティ get { //プロパティを利用すると実行される内容(無制限) return bw; } set { //プロパティに代入すると実行される内容(制限有り) if (value <= 0) { Console.WriteLine("体重に0または負の値は設定できません"); return; //プロパティの途中で呼ばれた相手に戻る } bw = value; //代入した値がvalueキーワード経由で渡される } } public double CalcBMI() { //通常のメソッド if (bl == 0.0 || bw == 0.0) { Console.WriteLine("データがセットされていません"); return 0.0; //無効でも値を返す必要がある } return bw / Math.Pow(bl, 2.0); //BMI値を計算して返す } public string answer { //【以下追加】読み込み専用のプロパティ get { return (bl > 0.0) ? "準備完了" : "未設定"; } } } class prop02 { public static void Main() { BMI mybmi = new BMI(); double bl, bw; do { //繰返し開始 Console.Write("身長(m)--- "); string strBl = Console.ReadLine(); bl = double.Parse(strBl); mybmi.blprop = bl; //プロパティのsetを用いて代入 } while (bl <= 0.0); //プロパティのgetが実行されて身長を得て比較 do { //繰返し開始 Console.Write("体重(kg)--- "); string strBw = Console.ReadLine(); bw = double.Parse(strBw); mybmi.bwprop = bw; //プロパティのsetを用いて代入 } while (bw <= 0.0); //プロパティのgetが実行されて体重を得て比較 Console.WriteLine("bl = {0}, bw = {1}", mybmi.blprop, mybmi.bwprop); //プロパティのget実行 Console.WriteLine("BMI = {0:#.#}", mybmi.CalcBMI()); //mybmi.answer = "OK"; //【追加・削除】setがないプロパティなので代入はエラー Console.WriteLine(mybmi.answer); //【追加】プロパティのgetが実行されて得た文字列を表示 } } p.211 プロパティ:静的プロパティ ・静的メソッドと同様に、staticを前置することで、静的プロパティを記述できる ・利用方法は他の静的メンバと同様で「クラス名.プロパティ名」となる ・静的データメンバを扱うプロパティは静的プロパティにすると良い アレンジ演習:p.211 prop03.cs ・Testクラスは静的クラスにできることを確認しよう 作成例 //アレンジ演習:p.211 prop03.cs using System; static class Test { //【変更】静的クラスにできる static int x; //静的データメンバ public static int myprop { //静的データメンバを扱う静的プロパティ get { return x; } set { x = value; } } } class prop03 { public static void Main() { Test.myprop = 10; //インスタンスを生成せずに「クラス名.プロパティ名」で呼び出せる Console.WriteLine("Test.myprop = {0}", Test.myprop); //同上 } } アレンジ演習:p.213 indexer01.cs ・インデクサが動作していることをコンソール出力を追加して確認しよう ・空文字列「""」が代入されたら、インデクサにおいて要素に代入しないようにしよう 作成例 //アレンジ演習:p.213 indexer01.cs using System; class MyClass { string[] name = new string[5]; //クラス内部でのデータ保持用の配列 public string this[int i] { //この配列を用いるインデクサ get { //インデクサを利用すると実行される内容(無制限) Console.WriteLine("インデクサ(get)実行 i = {0}, name = {1}", i, name[i]); //【追加】 return name[i]; } set { //インデクサに代入すると実行される内容 Console.WriteLine("インデクサ(set)実行 i = {0}, value = {1}", i, value); //【追加】 if (value != "") { //【追加】空文字列でなければ name[i] = value; //代入した値がvalueキーワード経由で渡される } } } } class indexer01 { public static void Main() { MyClass mc = new MyClass(); mc[0] = "一郎"; //インデクサのsetを呼んで配列に格納 mc[1] = "次郎"; //同上 mc[2] = "三郎"; //同上 mc[3] = "四郎"; //同上 mc[4] = "五郎"; //同上 mc[4] = ""; //【追加】空文字列なので、インデクサで却下される for (int i = 0; i < 5; i++) { Console.WriteLine(mc[i]); //インデクサのgetが実行されて文字列を得て表示 } } } p.212 インデクサ:インデクサによるインデックスのチェック ・インデクサのsetおよびgetでインデックスの値をチェックできるので、範囲外の添字を指定したアクセスを安全に排除できる アレンジ演習:p.214 indexer02.cs ・インデックスに負の数が与えられても異常終了しないようにインデクサで対処しよう 作成例 //アレンジ演習:p.214 indexer02.cs using System; class MyIndexer { int[] array; //クラス内部でのデータ保持用の配列(参照のみ) int nMax; //要素数(メソッドで設定) public int this[int n] { //インデクサ get { //インデクサを利用すると実行される内容 if (n < nMax && n >= 0){ //【変更】添字が要素数未満、かつ0以上なら return array[n]; //要素値を返す } else { //でなければ return 0; //0を返す } } set { //インデクサに代入すると実行される内容 if (n < nMax && n >= 0){ //【変更】添字が要素数未満、かつ0以上なら array[n] = value; //引数から要素[n]に格納 } } } public MyIndexer(int i) { //コンストラクタ array = new int[i]; //引数で受け取った要素数でデータ保持用の配列本体を生成 nMax = i; //引数で受け取った要素数をデータメンバに格納 } } class indexer02 { public static void Main() { MyIndexer mi = new MyIndexer(20); //コンストラクタに20が渡されて、要素数20の配列を生成 for (int i = 0; i < 20; i++) { //全要素について繰返す mi[i] = i * i; //添字の値の二乗を格納 } for (int i = 0; i < 20; i++) { //全要素について繰返す Console.WriteLine("{0} * {0} = {1}", i, mi[i]); } //わざとに配列の範囲を超える mi[30] = 30; //インデクサ経由なのでチェックされて却下 Console.WriteLine("mi[30] = {0}", mi[30]); //0のまま //【以下追加】わざとに添字を負の数にする mi[-3] = 30; //インデクサ経由なのでチェックされて却下 Console.WriteLine("mi[-3] = {0}", mi[-3]); //0のまま } } p.212 文字列をインデックスとするインデクサ ・整数ではないインデックスを受け取ってインデクサの中で配列の要素にアクセスする処理を記述することができる ・よって、文字列をインデックスとするインデクサや、実数をインデックスとするインデクサも可能になる アレンジ演習:p.217 indexer03.cs ・実数をインデックスとするインデクサを追加して、動作を確認しよう 作成例 //アレンジ演習:p.217 indexer03.cs using System; class MyIndexer { string[] mon = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; public int this[string MonthName] { //文字列をインデックスとするインデクサ get { for (int i = 0; i < 12; i++) { //配列の全要素について if (MonthName == mon[i]) { //月名と一致? return i + 1; //添字+1を返す } } return 0; //一つも一致しなかったら0を返す } } public string this[double n] { //【以下追加】実数をインデックスとするインデクサ get { if (n >= 1.0 && n < 13.0) { //1以上13未満であれば return mon[(int)n - 1]; //整数化して添字にして月名を返す } return ""; } } } class indexer03 { public static void Main() { MyIndexer mi = new MyIndexer(); Console.WriteLine("Mayは{0}番目の月です", mi["May"]); //5 Console.WriteLine("Decは{0}番目の月です", mi["Dec"]); //12 Console.WriteLine("xは{0}番目の月です", mi["x"]); //0 Console.WriteLine("mi[1.01] = {0}", mi[1.01]); //【追加】"Jan" Console.WriteLine("mi[12.9] = {0}", mi[12.9]); //【追加】"Dec" Console.WriteLine("mi[33.3] = {0}", mi[33.3]); //【追加】"" } } p.218 多次元のインデクサ ・2つ以上のインデクスを持つインデクサを記述できる ・2次元の書式: データ型 this[インデックス型① インデックス①, インデックス型② インデックス②] { get {…} set {…} } アレンジ演習:p.219 indexer04.cs ・インデクサのgetにインデックスの範囲チェックを加えよう ・インデックスが不適切な時は""を返す 作成例 //p.219 indexer04.cs using System; class MyClass { string[,] name; //クラス内部でのデータ保持用の2次元配列(参照のみ) public string this[int i, int j] { //2次元インデクサ get { if (i >= 0 && i < 2 && j >= 0 && j < 5) { //【追加】添字が範囲内? return name[i, j]; //名前を返す } return ""; //【追加】範囲外なら空文字列を返す } } public MyClass() { //コンストラクタ name = new string[,]{{"田中", "佐藤", "吉田", "加藤", "粂井"}, //1組1~5番 {"工藤", "竹中", "斉藤", "太田", "杉本"}}; //2組1~5番 } } class indexer04 { public static void Main() { MyClass mc = new MyClass(); for (int i = 0; i < 2; i++) { //全組について繰返す for (int j = 0; j < 5; j++) { //全番について繰返す Console.WriteLine("{0}組{1}番--{2}", i + 1, j + 1, mc[i, j]); } } Console.WriteLine("{0}組{1}番--{2}", 3, 3, mc[3, 3]); //【追加】 } } 作成例 //アレンジ演習:p.220 indexer05.cs using System; class MyOverLoad { int[] a = new int[3] { 1, 2, 3 }; //インデクサ①で扱う配列 int[,] b = new int[2, 2] { { 100, 200 }, { 300, 400 } }; //インデクサ②で扱う配列 public int this[int i] { //インデクサ① get { if (i >= 0 && i < a.Length) { //【追加】添字として適切かチェック return a[i]; //インデクサ①で扱う配列の要素値を返す } return 0; } } public int this[int i, int j] { //インデクサ② get { if (i >= 0 && i < 2 && j >= 0 && j < 2) { //【追加】添字として適切かチェック return b[i, j]; //インデクサ②で扱う配列の要素値を返す } return 0; } } public int this[double i] { //【以下追加】インデクサ③:インデックスが実数 get { return this[(int)i]; //インデックスを整数化することで、インデクサ①を呼ぶ } } } class indexer05 { public static void Main() { MyOverLoad mo = new MyOverLoad(); for (int i = 0; i < 3; i++) { Console.WriteLine("mo[{0}] = {1}", i, mo[i]); //インデクサ①を呼ぶ } for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { Console.WriteLine("mo[{0}, {1}] = {2}", i, j, mo[i, j]); //インデクサ②を呼ぶ } } //【以下追加】 Console.WriteLine("mo[9] = {0}", mo[9]); //インデクサ①を呼ぶ(範囲外なので0) Console.WriteLine("mo[9,8] = {0}", mo[9, 8]); //インデクサ②を呼ぶ(範囲外なので0) Console.WriteLine("mo[1.8] = {0}", mo[1.8]); //インデクサ③を呼ぶ Console.WriteLine("mo[9.8] = {0}", mo[9.8]); //インデクサ③を呼ぶ(範囲外なので0) } } p.222 練習問題1 ex0801.cs ヒント ・p.209 prop02.csを単純化すると良い ・double型のデータメンバ1つのみになる ・Mainメソッドで負の数や0を与えて動作を確認すること 作成例 //p.222 練習問題1 ex0801.cs using System; class BMI { double bw; //体重(publicがないのでprivate扱い) public double bwprop { //体重のプロパティ get { //プロパティを利用すると実行される内容(無制限) return bw; } set { //プロパティに代入すると実行される内容(制限有り) if (value <= 0) { Console.WriteLine("0または負の値は設定できません"); return; //プロパティの途中で呼ばれた相手に戻る } bw = value; //代入した値がvalueキーワード経由で渡される } } } class prop02 { public static void Main() { BMI mybmi = new BMI(); mybmi.bwprop = 73.2; //プロパティのsetを用いて代入 Console.WriteLine("bw = {0}", mybmi.bwprop); //プロパティのget実行 mybmi.bwprop = 0.0; //プロパティのsetを用いて代入(無視される) Console.WriteLine("bw = {0}", mybmi.bwprop); //プロパティのget実行(73.2のまま) mybmi.bwprop = -1.1; //プロパティのsetを用いて代入(無視される) Console.WriteLine("bw = {0}", mybmi.bwprop); //プロパティのget実行(73.2のまま) } } p.222 練習問題2 ヒント ex0802.cs ・p.217 indexer03.csをベースにすると良い ・コンストラクタで生徒数を入力する部分は、p.214 indexer02.csが参考になる ・生徒名を格納するstring型配列と、点数を格納するint型配列を宣言して用いると楽  ※ 生徒名と点数をメンバとするクラスオブジェクトの配列にできればさらに良い ・要素の生成はコンストラクタで行うと良い ・Mainメソッドで格納結果を表示すること 提出:p.222 練習問題2 ヒント ex0802.cs(未完成でもOK) 次回予告: ・p.223「第9章 継承」から