・p.166「メソッドを定義しよう(カプセル化)」から再開します
アレンジ演習:p.165 noreturnvalue.cs
・5つの「return;」を削除しても動作が変わらないことを確認しよう
作成例
//アレンジ演習:p.165 noreturnvalue.cs
using System;
class Kakeibo
{
private int total = 0; //残高:クラス内部でのみ用いる変数なのでprivateにする
public void nyukin(int en) { //入金メソッド(外部から用いるのでpublicにする)
total += en; //引数で得た入金額を残高に足し込む
Console.WriteLine("{0}円を入金しました", en);
// return; →次の行がメソッドの終端なので、このreturnは不要
}
public void shishutsu(int en) { //支出メソッド(外部から用いるのでpublicにする)
if (total < en) { //引数で得た支出額が残高を超えている?
Console.WriteLine("{0}円も支出できません", en);
// return; →次に実行する行がメソッドの終端なので、このreturnは不要
} else {
total -= en; //引数で得た支出額を残高から差し引く
Console.WriteLine("{0}円を支出しました", en);
// return; →次に実行する行がメソッドの終端なので、このreturnは不要
}
}
public void gettotal() { //残高を表示する(外部から用いるのでpublicにする)
if (total == 0) { //残高が0?
Console.WriteLine("残高はありません");
// return; →次に実行する行がメソッドの終端なので、このreturnは不要
} else {
Console.WriteLine("残高はは{0}円です", total); //残高の表示
// return; →次に実行する行がメソッドの終端なので、このreturnは不要
}
}
}
class noreturnvalue {
public static void Main() {
Kakeibo k = new Kakeibo();
k.gettotal(); //残高を表示
k.nyukin(1000);
k.gettotal(); //残高を表示
k.nyukin(2000);
k.gettotal(); //残高を表示
k.shishutsu(500);
k.gettotal(); //残高を表示
k.shishutsu(10000);
k.gettotal(); //残高を表示
}
}
p.166 メソッドを定義しよう(カプセル化)
・クラスのメンバのアクセス修飾子の基本は「クラス内部でのみ用いるものはprivate、でなければpublic」
・これに加えて、データメンバをprivateにすることで、データを適正な状態に保つことを「カプセル化」という。
・これを実現するには、データメンバをprivateにする代わりに、データメンバの値を操作するメソッドを用意してpublicにする
・この時、データメンバの値を操作するメソッドの中で値に対する制限をかけられるので、データを適正な状態に保つことが可能になる
・例:身長に0以下の数が格納されないようにする場合:
private height; //身長
public void setHeight(int h) { if (h > 0) { height = h; } } //正の数であれば身長に格納
・こうすると、外部からの「height = 〇」はエラーになり、setHeight(〇)とすれば良い
・しかも、setHeight(0以下の値)は、実行されないため、身長に0以下の値が入ってしまうことは防げる
・そして、このクラスを用いる処理では身長が0以下の可能性は無いことを前提にプログラムを記述できる
p.167 コンストラクタ
・全てのクラスがあらかじめ持っている特別なメソッド
・プログラマが必要に応じて記述することが可能で、そうでなければ自動的に中身が空のものが用意される
・これをデフォルトコンストラクタという
・よって、記述は任意
・メソッドだが、メソッド名はなく、戻り値型もなく、public固定
・コンストラクタはオブジェクトの生成時に自動的に呼び出されるもので、プログラマが任意に呼ぶことはできない
・よって、オブジェクトの生成時に行いたいことを記述するために用いる
例:インスタンス変数の初期化、リソースなどの準備処理 等
・定義書式①: public クラス名() {処理内容}
・システム側で必ず実行されるので、実行漏れを防ぎたい初期化処理などを記述すると良い
例:モンスターの初期HP、MPを生成時に乱数で決めたい
アレンジ演習:p.167 construct01.cs
・Main()の最終行で「MyClass md;」が実行できるが、コンストラクタが動作するかどうか ⇒宣言のみの記述は可能だがコンストラクタは呼ばれない ・これを「MyClass md = new MyClass();」と書き換えると、コンストラクタが動作するかどうか ⇒動作する
作成例
//p.167 construct01.cs
using System;
class MyClass {
int x;
public void showx() { //通常のpublicなメソッド
Console.WriteLine("x = " + x);
}
public MyClass() { //コンストラクタの定義
x = 10; //クラス変数の初期化
Console.WriteLine("xに10を代入しました"); //テスト用の表示
}
}
class construct01 {
public static void Main() {
MyClass mc = new MyClass(); //MyClassオブジェクトmcの生成とコンストラクタの実行
mc.showx(); //xは10になっている
//MyClass md; //宣言のみの記述は可能だがコンストラクタは呼ばれない
MyClass md = new MyClass(); //MyClassオブジェクトmdの生成とコンストラクタの実行
}
}
p.167 コンストラクタ:コンストラクタの引数
・メソッドと同様にコンストラクタにも引数が指定できる
・引数を指定したコンストラクタを呼び出すには、newにおいてカッコ内に引数型と引数を記述する
・定義書式②: public クラス名(引数型 引数名, …) {処理内容}
例: public Slime(int h, int p) { hp = h; mp = p; }
・呼び出し法: new クラス名(値や式, …);
・なお、コンストラクタで扱うクラス変数は、内部での処理なので、publicにする必要はない
アレンジ演習:p.167 construct01.cs つづき
・コンストラクタ「public MyClass() 」を「public MyClass(int i)」として、クラス変数xの初期値を与えるようにしよう ・呼び出すMain()において、クラス変数xの初期値を引数として与えること
作成例
//アレンジ演習:p.167 construct01.cs
using System;
class MyClass {
int x;
public void showx() { //通常のpublicなメソッド
Console.WriteLine("x = " + x);
}
public MyClass(int i) { //【変更】コンストラクタの定義
x = i; //【変更】クラス変数を引数の値で初期化
Console.WriteLine("xに{0}を代入しました", i); //テスト用の表示
}
}
class construct01 {
public static void Main() {
MyClass mc = new MyClass(10); //【変更】MyClassオブジェクトmcの生成とコンストラクタの実行
mc.showx(); //xは10になっている
MyClass md = new MyClass(20); //【変更】MyClassオブジェクトmdの生成とコンストラクタの実行
md.showx(); //xは20になっている
}
}
p.168 コンストラクタのオーバーロード
・オーバーロードとは同じ名前の要素を混在できることで、呼び出し方によって判別できれば許容される
・コンストラクタの場合、引数の数、型、並び順が異なるものを複数記述でき、これをコンストラクタのオーバーロードという
・例:
public Slime(){…} //hpが0、mpが0.0のスライムを生成
public Slime(int h){…} //hpがh、mpが0.0のスライムを生成
public Slime(int h, double m){…} //hpがh、mpがmのスライムを生成
アレンジ演習:p.169 construct02.cs
・コンストラクタのオーバーロードとして「public MyClass(string, int)」を追記し、氏名と年齢を引数で初期化し、住所は"不定"とする ・このコンストラクタの動作を確認する処理を追加しよう
作成例
//アレンジ演習: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 str) { //コンストラクタ①(string) 氏名のみ
name = str; //氏名は引数から
address = "不定"; //住所は固定
age = -1; //年齢も固定
}
public MyClass(int x) { //コンストラクタ②(int) 年齢のみ
age = x; //年齢は引数から
name = "不明"; //氏名は固定
address = "不定"; //住所も固定
}
public MyClass(string str1, string str2, int x) { //コンストラクタ③(string,string,int) 全て
name = str1; //氏名は引数1から
address = str2; //住所は引数2から
age = x; //年齢は引数3から
}
public MyClass(string str, int x) { //【追加】コンストラクタ④(string,int) 氏名と年齢
name = str; //氏名は引数から
address = "不定"; //住所は固定
age = x; //年齢も固定
}
}
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(); //【追加】
}
}
p.171 コンストラクタのオーバーロード(続き)
・プログラマが1つもコンストラクタを記述しないと、デフォルトコンストラクタが内部的に用意される ・これは、new クラス名(); とした時に呼び出されるようになっている(中身がないので見かけ上は何もしない) ・しかし、プログラマが1つでも引数有りのコンストラクタを記述すると、デフォルトコンストラクタは用意されない ・よって、必要に応じて、デフォルトコンストラクタを記述する必要がある
アレンジ演習:p.169 construct02.cs つづき
・Main()でデフォルトコンストラクタを呼ぶような処理を追記し、エラーになることを確認しよう ・それから、自前のデフォルトコンストラクタを記述し、氏名は"不明"、住所は"不定"、年齢は-1 としよう
作成例
//アレンジ演習: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 str) { //コンストラクタ①(string) 氏名のみ
name = str; //氏名は引数から
address = "不定"; //住所は固定
age = -1; //年齢も固定
}
public MyClass(int x) { //コンストラクタ②(int) 年齢のみ
age = x; //年齢は引数から
name = "不明"; //氏名は固定
address = "不定"; //住所も固定
}
public MyClass(string str1, string str2, int x) { //コンストラクタ③(string,string,int) 全て
name = str1; //氏名は引数1から
address = str2; //住所は引数2から
age = x; //年齢は引数3から
}
public MyClass(string str, int x) { //【追加】コンストラクタ④(string,int) 氏名と年齢
name = str; //氏名は引数から
address = "不定"; //住所は固定
age = x; //年齢も固定
}
public MyClass() { //【追加】デフォルトコンストラクタ
age = -1; //年齢は固定
name = "不明"; //氏名も固定
address = "不定"; //住所も固定
}
}
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(); //【追加】
}
}