前回のコメント

・ガベージコレクションやデストラクションですが、つまりは今までに扱ってきたC#コード内でもすでに自動的に実行されていたということでしょう

 ガベージコレクションは実行中にメモリ不足が懸念される場合に実行されますので、
 入門編のような小さなプログラムではあまり実行されていないかもしれませんが、
 実行されている可能性もあります。
 デストラクタについては、newによる生成を行ったプログラムの終了時には必ず実行されています。

・「this」は今回紹介していただいた応用例の他には、これといった使い道はないのでしょうか。

 後日、学習する「インデクサ」が大きな用途の一つです。
 また、今回は構文説明の都合で割愛しましたが、コンストラクタからコンストラクタを呼ぶことができます。
 この件は、次回フォローしますね。

講義メモ 後半

p.171(ガベージコレクション)

・プログラムの中で生成されたオブジェクトは、基本的に終了時に破棄される
・言語によっては、プログラムの中で動的に記憶領域を確保したり破棄できるものもある
・しかし、C#などでは、オブジェクトの生成のみが可能で、プログラム側で破棄することはできない
・そこで、C#などでは、オブジェクトを参照する変数を無くすることで、オブジェクトの利用を終了したことを、システムに示すことができる
・システムは定期的な監視により、利用が終了したオブジェクトを破棄する。これをガベージコレクションという
・例:MyClass a = new MyClass(), b = new MyClass(); a = b; //最初にa用に生成したオブジェクトは破棄対象
・例:MyClass a = new MyClass(), a = null; //a用に生成したオブジェクトは破棄対象
・この仕組みを理解することで、非常に多くのデータを用いる処理において、使っていないオブジェクトが終了時まで残ってしまうことを
 防止できる。

p.171 デストラクタ

・オブジェクトの生成時に呼ばれるコンストラクタとは逆に、オブジェクトの破棄時に呼ばれるのがデストラクタ
・デストラクタはオブジェクトの生成時に中身が空のものが自動生成されて呼び出されるが、プログラマの記述も可能
・ただし、直接呼び出したり、動作タイミングを制御することはできず、システム任せになる
・よって、主に、後始末に用いると良い
・定義書式: ~クラス名(){…}
・デストラクタはコンストラクタと同様に、戻り値はない(voidの指定も不可)
・しかも、引数も指定できないので、オーバーロードもできない
・プログラムの終了時のように複数のオブジェクトが破棄される時、すべてのデストラクタが確実に実行されるが、その実行順序は不定であり、
 プログラムから知ることもできない

アレンジ演習:p.173 destruct01.cs

・変数dt2、dt3を用いずに、3つのオブジェクトを全て変数dt1で扱うとどうなるか確認しよう ⇒ 動作はアレンジ前と変わらない
・タイミングを見るために「Console.ReadLine();」を末尾に入れて、表示を確認後、Enterキーを入力して終了させよう

作成例

//アレンジ演習:p.17 destruct01.cs
using System;
class DestructTest {
    int x;
    // デストラクタ
    ~DestructTest() {
        Console.WriteLine("デストラクタが呼ばれました");
        Console.WriteLine("xは{0}です", x);
    }
    // 引数付きコンストラクタ
    public DestructTest(int n) {
        Console.WriteLine("コンストラクタが呼ばれました");
        x = n; 
        Console.WriteLine("xに{0}を代入しました", n);
    }
}
class destruct {
    public static void Main() {
        //コンストラクタが呼ばれ1を持つオブジェクトを生成
        DestructTest dt1 = new DestructTest(1); 
        Console.WriteLine("dt1生成");
        //コンストラクタが呼ばれ2を持つオブジェクトを生成、1を持つオブジェクトは破棄対象になる
        dt1 = new DestructTest(2); //【変更】
        Console.WriteLine("dt2生成");
        //コンストラクタが呼ばれ3を持つオブジェクトを生成、2を持つオブジェクトは破棄対象になる
        dt1 = new DestructTest(3); //【変更】 
        Console.WriteLine("dt3生成");
        Console.ReadLine(); //【追加】終了待ち
    }
}

アレンジ演習:p.173 destruct01.cs

・50,000回の繰返しにして50,000個のオブジェクトを全て変数dt1で扱うとどうなるか確認しよう
 ⇒ 生成中にメモリ不足が発生するとみなされて、生成と並行して破棄が行われる(可能性が高い)
 ⇒ よって、コンストラクタのメッセージとデストラクタのメッセージが混在する(可能性が高い)

作成例

//アレンジ演習:p.17 destruct01.cs
using System;
class DestructTest {
    int x;
    // デストラクタ
    ~DestructTest() {
        Console.Write("デストラクタが呼ばれました");
        Console.WriteLine("xは{0}です", x);
    }
    // 引数付きコンストラクタ
    public DestructTest(int n) {
        Console.Write("コンストラクタが呼ばれました");
        x = n; 
        Console.WriteLine("xに{0}を代入しました", n);
    }
}
class destruct {
    public static void Main() {
        DestructTest dt1 = new DestructTest(0); 
        for(int i = 1; i < 50000; i++) { //【追加】
            dt1 = new DestructTest(i); //【変更】
        }
        Console.ReadLine(); //【追加】終了待ち
    }
}

p.174 this

・自分自身を指す参照をthisで得ることができる
・よって、自クラス型の変数を宣言しておいて、thisを代入できる
 ⇒ p.174 this01.cs
・このこと自体には実用性がないが、下記の応用例において、活用される

p.174 補足:thisの応用例① クラス変数の値を書き換えた結果を返す

・こうすると返された自オブジェクトへの参照をそのまま利用できる
・例:
 class Monster {
  public int hp, mp;
  public Monster hpx(int d) { hp *= d; return this; } //HPをd倍した結果のオブジェクトを返す
  public Monster mpx(int d) { mp *= d; return this; } //MPをd倍した結果のオブジェクトを返す
 }
 :
 Monster slalin = new Monster();
 slalin.hp = 10;
 slalin.mp = 20;
 slalin.hpx(5).mpx(2); //HPを5倍してからMPを2倍する

p.174 補足:thisの応用例② メソッドの引数とインスタンス変数を同じ名前にして識別する

・こうするとメソッドの引数にインスタンス変数と異なる名前を付ける必要がなくなる
・例:
 class Monster {
  public int hp, mp;
  public setHP(int hp) { this.hp = hp; } //this.hpはインスタンス変数、hpは引数
  public setMP(int mp) { this.mp = mp; } //this.hpはインスタンス変数、hpは引数
 }

提出:アレンジ演習:p.173 destruct01.cs (どのバージョンでもOK)

講義メモ

・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(); //【追加】
    }
}

今週の話題

ゲームソフト販売本数ランキング:今週1位も「マリオカート ワールド(Switch2)」 GO!
SIEがPlayStation Studiosタイトルのマルチプラットフォーム展開を推進か?米国向け求人で担当者募集中 GO!
バンダイナムコとソニーが戦略的なパートナーシップを締結!マンガ・アニメなど幅広い領域での協業に取り組む GO!
ゲーム業界の平均年収ランキングTOP20をSalesNowが公開、バンダイナムコや任天堂を上回った1位の企業は… GO!

サ終済みのモバイル向けRPG『BLUE REFLECTION SUN/燦』元公式サイトには接続しないで!第三者がドメイン取得し不審なWebサイトへの誘導設置 GO!
Steamでプレイテスト実施中だったゲームにマルウェア混入との告発―該当ストアページは既に閲覧できない状態に GO!
Steamストアからの成人向けゲーム削除、7月26日時点でも流れは止まらず…新たに削除されたタイトル多数 GO!

前回のコメント

・5つの「return;」を削除しても動作が変わらないことを確認しました。

 了解です。何よりです。

・privateとpublicはどう使い分けると良いのでしょうか。

 基本は「クラス内部でのみ用いるものはprivateに、でなければpublicに」します。
 これに加えて、次回学習する「カプセル化」で、privateにすることで、データを適正な状態に保つことを学びます。

・if文とswich文とdo文は役割が似ているようですが、仮にどれでも使える状況だったとしても、やはり状況に応じて最も適した物を使用したほうが好ましいでしょうか。

 if文は条件による分岐に、switch文は値による多分岐に用います。
 do文は分岐ではなく繰返し構文です。
 よって「仮にどれでも使える状況」ということはあり得ません。
 switch文の代わりにif文を用いることはできますが、それ以外は取り換えの難しい構文です。
 用途に合わせて使い分けることが肝心です。

講義メモ 後半

p.160 メソッドを定義しよう

・Mainメソッドのような処理(振る舞い)の記述を、実行用ではないクラスにも記述できる
・メソッドは0個以上の情報を得て、なにか処理を行い、0または1個の情報を返すことができる仕掛け
・例: Addメソッドに1と2を渡すと、加算処理が動作して、3を返す
・メソッドの定義書式: アクセス修飾子 戻り値型 メソッド名(引数型 引数名, …){処理内容}
・アクセス修飾子:メソッドをクラスの外から呼び出す場合はpublicを指定(よって、Mainメソッドはpublic)
 何も指定しないか、privateを指定すると、内部用になる(クラス内の別のメソッドからは利用可能)
・戻り値型(復帰型)は返す情報のデータ型を記述する。なお、ない場合は voidを指定する(よって、Mainメソッドは基本的にvoid)
・引数:メソッドに渡す情報で、0個以上いくつでも記述でき「データ型 引数名」をカンマつなぎする
・なお、0個の場合はカッコのみ記述する(よって、MainメソッドはMain()が基本)
・処理内容では、戻り値型が指定してある時は「return 値や式;」が必須になり、分岐などを含むときは、どの分岐になっても必ずreturnが
 実行される必要がある。
・外部からメソッドを実行するには、インスタンス変数と同様に「オブジェクト名.メソッド名(…)」とする
・例:
 class Monster { //クラスの定義
  public int hp; //インスタンス変数の定義
  public void showhp() { Console.WriteLine("HPは{0}", hp); } //メソッドの定義
 }
 :
 class Game { //実行用クラス
  public static void Main() { //実行用の特別なメソッド
   Monster slalin = new Monster(); //Monsterオブジェクトを生成してスラリンとする
   slalin.hp = 10; //スラリンのインスタンス変数hpに代入
   slalin.showhp(); //スラリンのメソッドshowhp()を実行
  }
 }
・なお、returnの直後は(if-else構文等の場合を除き)実行されないので何かを記述するとエラーになる

アレンジ演習:p.161 method01.cs

・2つのint型の引数を受け取り、差をint型で返すSubメソッドを追記しよう
・Main()から、Subメソッドを呼び出して動作を確認しよう
・なお、ローカル変数を経由しないことで、シンプルに記述すること

作成例

//アレンジ演習:p.161 method01.cs
using System;
class MyClass //クラスの定義
{
    public int Add(int x, int y) //int型の2引数を受け取り、int型を返すAddメソッドの定義
    {
        int z; //ローカル変数(メソッドの中で宣言され、メソッド内のみ有効な作業用の変数)
        z = x + y; //2引数の和を求めてローカル変数zに代入
        return z; //ローカル変数z経由で和を返す
    }
    public int Sub(int x, int y) { //【以下追加】int型の2引数を受け取り、int型を返すSubメソッドの定義
        return (x > y) ? x - y : y - x; //xとyで大きいほうから小さいほうを引いた差を返す
    }
}
class method01 //実行用クラス
{
    public static void Main() //実行用の特別なメソッド
    {
        MyClass a = new MyClass(); //MyClassオブジェクトを生成してaとする
        int sum; //合計用のローカル変数
        sum = a.Add(100, 200); //100と200を引数としてオブジェクトaのAddメソッドに渡し、戻り値を代入
        Console.WriteLine("sum = {0}", sum); //戻り値として和を代入されているローカル変数の値を表示
        Console.WriteLine("sub = {0}", a.Sub(100, 200)); //【追加】直接メソッドを呼び100と200の差を表示
    }
}

p.163 メソッドを定義しよう(インスタンスメソッド)

・method01.csのAdd(int,int)メソッドなどは、インスタンスに含まれて生成されるので、インスタンスメソッドともいう
・ちなみに、Main()メソッドはインスタンスに含まれない特殊なメソッド(p.202「静的メソッド」に該当する)
・インスタンスメソッドは同じインスタンスに属するインスタンス変数の値を無条件に(変数名だけで)得ることができる
・例:p.163 bmiclassにおける、BMIクラスのインスタンス変数blmを、インスタンスメソッドCalcで用いている
・なお、インスタンス変数blmのように、インスタンスメソッドからのみ利用される変数(外部からアクセスされない変数)は、
 アクセス修飾子を「private」にして保護すると良い

アレンジ演習:p.163 bmiclass.cs

・身長、体重に0以下の値が入力されたら、先に進まずに、再入力させるようにしよう

作成例

//アレンジ演習:p.163 bmiclass.cs
using System;
class BMI //クラスの定義
{
    private double blm; //身長(m単位)、インスタンス変数でprivate(内部用)
    public double Calc(double bl, double bw) //double型の2引数を受け取り、double型を返すメソッドの定義
    {
        blm = bl / 100.0; //cm単位である引数blを100で割ってm単位にしてインスタンス変数に保持
        return bw / Math.Pow(blm, 2.0); //BMIを算出して返す
    }
}
class bmiclass
{
    public static void Main()
    {
        string strBl, strBw; //入力用身長、体重
        double blcm, bwkg; //実数返還後の身長、体重
        do { //【追加】
            Console.Write("身長(cm)---");
            strBl = Console.ReadLine();
            blcm = Double.Parse(strBl); //文字列である画面入力を実数化
        } while (blcm <= 0); //【追加】
        do { //【追加】
            Console.Write("体重(kg)---");
            strBw = Console.ReadLine();
            bwkg = Double.Parse(strBw); //文字列である画面入力を実数化
        } while (bwkg <= 0); //【追加】
        BMI bmi = new BMI(); //BMIクラスのインスタンスを生成
        Console.WriteLine("BMIは{0:#.##}です", bmi.Calc(blcm, bwkg)); //オブジェクト名経由で値を受渡す
    }
}

p.164 メソッドを定義しよう(戻り値のないメソッド、引数のないメソッド)

・戻り値のないメソッドには「void」を指定する
・中で「return 値や式」を実行することはできないが、即時にメソッドの動作を打ち切って、呼び出し元に戻る「return;」は利用可能
・また、複数記述しても良く、分岐によりreturnが実行されたない経路が有っても良い
・ただし、p.165 noreturnvalue.csにおける5つの「return;」はどれも次に実行すべき文がないので無意味で冗長。
・引数のないメソッドでは、定義時と呼び出し時に「メソッド名()」とし「()」は省略不可。
 ※ C/C++のように「(void)」とすると文法エラーになる

アレンジ演習:p.165 noreturnvalue.cs

・5つの「return;」を削除しても動作が変わらないことを確認しよう

提出:アレンジ演習:p.165 noreturnvalue.cs

講義メモ

・p.155 簡単なクラスを定義しよう(データメンバの利用)から

p.154 簡単なクラスを定義しよう【再掲載】

・クラスにはデータや実行用のメソッドなどを含むことができる
・ここまで作成したプログラムにおいては、1クラスの中にmainという1つのメソッドが含まれている
・クラスに含まれるデータや実行用のメソッドなどをメンバという
・今まで定義してきた変数はすべてmainメソッドの中だったが、mainメソッドの外に定義することで、変数をクラスのメンバ(データメンバ)に
 することができる
・これをクラス変数といい、メソッドとは独立して扱うことができる
・定義書式: アクセス修飾子 データ型 変数名;
・アクセス修飾子はメンバをクラスの外部から利用可能にするかどうか等を指定するもので、publicにすると、外部から利用可能
・mainメソッドがpublic指定なのは、C#システムから呼び出し可能にするため
・外部から利用可能にする必要がなければ、privateとすることで、クラス内でのみ利用可能にでき、安全性が高くなる
・クラスの定義書式: class クラス名 { メンバの定義; … }
・クラスのメンバはクラス変数のみでも良いので、複数の型が異なる変数をまとめて扱う場合にも有効
 ※ C言語における構造体をより強力に実装できる
・クラス変数のみのクラスの例
 class Monster { //モンスタークラス(名前とHPとMPをデータメンバとして持つ)
  public string name;
  public int hp;
  public int mp;
 }

p.155 簡単なクラスを定義しよう(クラスからのオブジェクトの生成)【リライト】

・クラス変数のうち、インスタンスに属するものをインスタンス変数といい、特例を除き、クラス変数はインスタンス変数にする
・クラスの定義のみでは、基本的に設計図にすぎないので、記述したインスタンス変数の実体はない
・クラスからオブジェクト(インスタンス)を生成することにより、インスタンス変数の実体が用意される
・クラスからオブジェクト(インスタンス)を生成して扱うには、オブジェクトを識別する名前が必要で、これをオブジェクト名または
 インスタンス名という
・オブジェクト名は、クラスを型とする変数の扱いになる
 例: Monster slarin; //モンスタークラスを型とするオブジェクトslarinの宣言
・そして、クラスからオブジェクト(インスタンス)を生成するにはnew演算子を用い、生成したオブジェクトをオブジェクト名の変数に代入する
 形式になる
・書式: オブジェクト名 = new クラス名(…); //クラスからオブジェクトを生成しオブジェクト名で利用可能にする
 例: slarin = new Monster(); //モンスタークラスのオブジェクトを生成し、オブジェクト名slarinで利用可能にする
・変数の初期化と同様に、オブジェクト名の宣言とオブジェクトの生成は同時に行える
・書式: クラス名 オブジェクト名 = new クラス名(…); //クラスからオブジェクトを生成しオブジェクト名で利用可能にする

p.155 簡単なクラスを定義しよう(データメンバの利用)

・これにより、インスタンス変数の実体が生成され「オブジェクト名.インスタンス変数名」で利用可能になる
 例: slarin.name = "スラリン"; slarin.hp = 5; slarin.mp = 10;
・このドットは「の」のイメージで、ドット演算子ともいう

アレンジ演習:p.156 simpleclass.cs

・インスタンス変数としてdouble型のyを追加し、Mainメソッドにyを扱う処理を追記しよう

作成例

//アレンジ演習:p.156 simpleclass.cs
using System;
class myclass //プログラマによる自前のクラスの定義
{
    public int x; //メンバ:インスタンス変数
    public double y; //【追加】メンバ:インスタンス変数
}
class simpleclass //実行用クラス(Main()メソッドを持つ)
{
    public static void Main()
    {
        myclass mc = new myclass(); //自前のクラスのオブジェクトを生成しインスタンス名mcで利用可能に
        mc.x = 10; //インスタンス名.インスタンス変数を用いて、値を代入
        Console.WriteLine("mc.x = {0}", mc.x); //インスタンス名.インスタンス変数を用いて、値を表示
        //【以下追加】
        mc.y = 3.14; //インスタンス名.インスタンス変数を用いて、値を代入
        Console.WriteLine("mc.y = {0}", mc.y); //インスタンス名.インスタンス変数を用いて、値を表示
    }
}

p.157 簡単なクラスを定義しよう(インスタンス変数の複数定義・生成)

・「int x, y;」というように、同じ型の変数をまとめて宣言できる
・インスタンス名も同様で「クラス名 インスタンス名①, インスタンス名②, …」と記述できる
・なお、インスタンスの生成もまとめて行える(「int x = 1, y = 2;」と同様に)
・書式例: クラス名 インスタンス名① = new クラス名(), インスタンス名② = new クラス名(), …;

アレンジ演習:p.157 simpleclass02.cs

・014行目~016行目の内容を1文にしよう

作成例

//アレンジ演習:p.157 simpleclass02.cs
using System;
class MyClass //プログラマによる自前のクラスの定義
{
    public int x; //メンバ:インスタンス変数
}
class simpleclass02 //実行用クラス(Main()メソッドを持つ)
{
    public static void Main()
    {
        MyClass a = new MyClass(), b  = new MyClass(); //【変更】インスタンスを2つ生成
        a.x = 10; //インスタンスaのデータメンバxに代入
        b.x = 100; //インスタンスbのデータメンバxに代入(↑とは別物)
        Console.WriteLine("a.x = {0}, b.x = {1}", a.x, b.x); //異なる値になっている
    }
}

参考:クラス図とオブジェクト図

・オブジェクト指向において各種の図解を行う図の標準化が行われており、UML(ユニファイド(統合された)モデリングランゲージ)という
・UMLに含まれる図として、クラス図とオブジェクト図がある
・作成には、 https://www.drawio.com/ が便利
・クラス図とオブジェクト図

p.158 simpleclass03.cs 解説

・MyClassクラス、そのインスタンス、オブジェクト名aとbの関係を図示すると下記のようになる
 

アレンジ演習:p.158 simpleclass03.cs

・末尾において、新たにMyClassクラスのインスタンスを生成してオブジェクト名bで扱うようにしよう
・そして、b.xに1000を代入すると、a.xはどうなるか確認しよう

作成例

//アレンジ演習:p.158 simpleclass03.cs
using System;
class MyClass //プログラマによる自前のクラスの定義
{
    public int x; //メンバ:インスタンス変数
}
class simpleclass03 //実行用クラス(Main()メソッドを持つ)
{
    public static void Main()
    {
        MyClass a, b; //オブジェクト名を2つ宣言
        a = new MyClass(); //インスタンスを生成し、オブジェクト名aで参照する
        a.x = 10; //インスタンスaのデータメンバxに代入
        b = a; //インスタンスaをオブジェクト名bで参照する(aとbは同じインスタンスを参照する)
        Console.WriteLine("b.x = {0}", b.x); //10になる
        b.x = 100; //インスタンスbのデータメンバxに代入
        Console.WriteLine("a.x = {0}", a.x); //同じインスタンスを参照しているので100になる
        //【以下追加】
        b = new MyClass(); //インスタンスを生成し、オブジェクト名bで参照する(aとbは違う参照になる)
        b.x = 1000; //インスタンスbのデータメンバxに代入
        Console.WriteLine("a.x = {0}", a.x); //同じインスタンスを参照していないので100のままになる
    }
}
解説図
 

p.158 簡単なクラスを定義しよう(実行用クラスとインスタンス変数)

・Main()メソッドを持つ実行用クラスに、インスタンス変数を持つことが可能
・これは、クラスのテスト用や利用方法の説明用にMain()メソッドを付け加える場合を除いて、推奨されない
 (よって、simpleclass04.csは割愛)
・なお、Main()メソッドから直接、インスタンス変数にアクセスすることはできず、オブジェクトを生成する必要がある
・この理由は、p.202「静的メソッド」にて。

今週の話題

ゲームソフト販売本数ランキング:今週1位も「マリオカート ワールド(Switch2)」 GO!
GameWithは四半期黒字転換に成功、ゲームエイトは成長限界が見えたか?【ゲーム企業の決算を読む】GO!
クリエイター向けマーケット「BOOTH」、AI生成作品への対応を強化―著作権侵害や大量出品にアカウント停止を含む厳格対処 GO!

相次ぐSteam成人向け削除は「クレジット決済代行業者や銀行の要請によるもの」Valve、弊誌らに回答 GO!
『妖怪ウォッチ』偽通販サイトに注意!レベルファイブが「一切関係ございません」として注意喚起 GO!