・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.212 インデクサ
・主に、配列をデータメンバとして持つクラスにおいて「オブジェクト名[添字]」とすることで要素を扱える仕組みがインデクサ
・アクセッサと似た記述が可能なので、データの保護にも役立つ
・定義書式: データ型 this[インデックス型 インデックス] { get {…} set {…} }
・getの基本構文は「get { return 配列名[インデックス]; }」
・setの基本構文は「set { 配列名[インデックス] = value; }」
アレンジ演習: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のまま
}
}