講義メモ

・p.212「インデクサ」の補足とp.213「index01.cs」から

p.212 インデクサ(再掲載)

・主にデータメンバである配列等をプロパティと同様に扱う仕掛けがインデクサ
・プロパティとは異なり、インデクサには名前がなく「インスタンスの参照変数[添字]」の形式で、配列の要素を扱える
・定義書式: public 型 this[インデックス型 インデックス] { get {…; return 値や式;} set {…;} }
 ※ 型は扱いたい配列等に合わせる
 ※ getの中において配列等の要素[インデックス]の値を返す
 ※ setの中では外部から与えられる値をvalueキーワードで得ることができ、通常、これを要素[インデックス]に代入
・基本的な定義書式: public 型 this[int i] { get {return 配列名[i];} set {配列名[i] = value;} }
・代入式の左辺に参照変数[添字]を指定すると、setの内容が実行される。例:参照変数[0] = 値;
・代入式の左辺以外に参照変数[添字]を指定すると、getの内容が実行される。例:Console.Write(参照変数[0]);

p.213 indexer01.cs

//p.213 indexer01.cs
using System;
class MyClass {
    string[] name = new string[5]; //インデクサで扱いたい配列
    public string this[int i] { //インデクサの定義(インデックスは整数のi)
        get { return name[i]; }
        set { name[i] = value; }
    }
}
class indexer01 {
    public static void Main() {
        MyClass mc = new MyClass();
        mc[0] = "一郎"; //インデクサ経由でname[0]に代入
        mc[1] = "次郎"; //インデクサ経由でname[1]に代入
        mc[2] = "三郎"; //インデクサ経由でname[2]に代入
        mc[3] = "四郎"; //インデクサ経由でname[3]に代入
        mc[4] = "五郎"; //インデクサ経由でname[4]に代入
        for (int i = 0; i < 5; i++) {
            Console.WriteLine(mc[i]); //インデクサ経由でname[0]~[4]を得て表示
        }
    }
}

p.214(インデクサで扱う構造を動的に生成したい場合)

・インデクサで扱いたいデータ構造(例:配列)の実体はクラス内に固定長で配置する必要はない。
・参照のみを定義しておいて、コンストラクタ等で要素数の分だけ生成すれば良い。
・ただし、要素数を保持する仕掛けが必要。

p.214 indexer02.cs

//p.214 indexer02.cs
using System;
class MyIndexer {
    int[] array; //インデクサで扱いたい配列の定義(未生成)
    int nMax; //要素数
    public int this[int n] { //インデクサの定義(インデックスは整数のi)
        get {
            if (n < nMax) { //要素数チェック
                return array[n];
            } else {
                return 0;
            }
        }
        set {
            if (n < nMax) { //要素数チェック
                array[n] = value;
            }
        }
    }
    public MyIndexer(int i) { //コンストラクタ(要素数)
        array = new int[i]; //要素数の分の要素を生成
        nMax = i; //要素数をセット
    }
}
class indexer02 {
    public static void Main() {
        MyIndexer mi = new MyIndexer(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; //配列ならば実行時エラーだが、インデクサのsetに30が渡されるだけ
        Console.WriteLine("mi[30] = {0}", mi[30]);
    }
}

p.217 文字列をインデックスとするインデクサ

・インデクサのインデックスは整数値である必要はない
・インデクサの定義の「public int this[int n] {…}」のインデックスであるnの型はintでなくても良い

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のみ)
        get {
            for (int i = 0; i < 12; i++) { //文字列配列の全要素について繰り返す
                if (MonthName == mon[i]) { //インデックスと一致したら
                    return i + 1; //添字に+1して月にして返す
                }
            }
            return 0; //当てはまるものがなければ0を返す
        }
    }
}
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
    }
}

p.218 多次元のインデクサ

・インデクスを複数持つ多次元のインデクサを定義できる
・多次元配列と同様に利用できる

p.219 indexer04.cs

//p.219 indexer04.cs
using System;
class MyClass {
    string[,] name; //インデクサで扱いたい2次元配列の定義(未生成)
    public string this[int i, int j] { //2次元インデクサ(setは無い)
        get {
            return name[i, j]; //2次元配列の要素を返す
        }
    }
    public MyClass() { //コンストラクタ
        name = new string[,]{{"田中", "佐藤", "吉田", "加藤", "粂井"},
                             {"工藤", "竹中", "斉藤", "太田", "杉本"}}; //2×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]); //2次元のインデクサで呼ぶ
            }
        }
    }
}

p.220 インデクサのオーバーロード

・インデクサは名前が無いので、インデックスの数と型がシグニチャとなる
・よって、インデックスの数と型が異なるインデクサが複数定義できる
・これをインデクサのオーバーロードという
・メソッドの場合と同様に、戻り値型はシグニチャに含まれないので注意。

p.220 indexer05.cs

//p.220 indexer05.cs
using System;
class MyOverLoad {
    int[] a = new int[3] { 1, 2, 3 }; //インデクサ①で扱う1次元配列
    int[,] b = new int[2, 2] { { 100, 200 }, { 300, 400 } }; //インデクサ②で扱う2次元配列
    public int this[int i] { //インデクサ①
        get { return a[i]; } //1次元配列の要素[インデックス]を返す
    }
    public int this[int i, int j] { //インデクサ②
        get { return b[i, j]; } //2次元配列の要素[インデックスi,インデックスj]を返す
    }
}
class indexer05 {
    public static void Main() {
        MyOverLoad mo = new MyOverLoad();
        for (int i = 0; i < 3; i++) { //1次元配列の様子数の分、繰り返す
            Console.WriteLine("mo[{0}] = {1}", i, mo[i]);
        }
        for (int i = 0; i < 2; i++) { //2次元配列の外側の様子数の分、繰り返す
            for (int j = 0; j < 2; j++) { //2次元配列の内側の様子数の分、繰り返す
                Console.WriteLine("mo[{0}, {1}] = {2}", i, j, mo[i, j]);
            }
        }
    }
}

アレンジ演習:p.220 indexer05.cs

・インデックスが整数のインデクサと文字列のインデクサが共存できることを確認しよう

作成例

//アレンジ演習:p.220 indexer05.cs
using System;
class MyOverLoad {
    string[] a = new string[3] { "amuro", "shar", "ryu" }; //インデクサ①②で扱う1次元配列
    public string this[int i] { //int型のインデクサ①
        get { return a[i]; } //配列の要素[インデックス]を返す
    }
    public int this[string s] { //string型のインデクサ②
        get { //配列の要素[添字]と一致したら添字を返す
            for (int i = 0; i < a.Length; i++) {
                if(a[i] == s) return i; 
            }
            return -1; //一致するものがなければ-1を返す
        } 
    }
}
class indexer05 {
    public static void Main() {
        MyOverLoad mo = new MyOverLoad();
        Console.WriteLine("mo[1] = {0}", mo[1]); //int型のインデクサ①を用いる
        Console.WriteLine("mo[\"shar\"] = {0}", mo["shar"]); //string型のインデクサ②を用いる
    }
}

p.222 練習問題1 ヒント

・p.209 prop02.csを単純化すると良い
・double型のデータメンバを1個のみ持つクラスとし、2つ目のプロパティやBMI計算は割愛する
・double型のデータメンバに適当な初期値を与えておき、プロパティで負の数や0を与えて、値が変化しないことを確認しよう

作成例

//p.222 練習問題1
using System;
class BMI {
    double bl; //非公開のインスタンス変数:身長
    public double blprop { //身長用のプロパティ
        get { return bl; }
        set {
            if (value <= 0) { //代入値が0以下?
                return; //終わって戻る
            }
            bl = value; //身長に代入値を代入
        }
    }
}
class prop02 {
    public static void Main() {
        BMI mybmi = new BMI();
        mybmi.blprop = 175.2; //プロパティ経由でsetを実行
        Console.WriteLine("bl = {0}" ,mybmi.blprop); //プロパティで身長を得て表示
        mybmi.blprop = -90.3; //プロパティ経由でsetを実行(負の数なので設定されない)
        Console.WriteLine("bl = {0}" ,mybmi.blprop); //プロパティで身長を得て表示
    }
}

p.222 練習問題2 ヒント

・問題文に示されていない仕様は自由に決めて良い。下記は一例。
・生徒数=配列の要素数をコンストラクタ(int)で受け取る
・string型の生徒名の配列を初期化する
・同じ要素数のint型の点数の配列を生成する
・インデクサ[string]を定義する
 ・getでは生徒名の配列を調べて、あればその添字を用いて、点数の配列[添字]の値を返す
 ・なければ(生徒名がnullになったら)、-1を返す
 ・setでは生徒名の配列を調べて、あればその添字を用いて、点数の配列[添字]の値をvalueに変更
 ・なければ(生徒名がnullになったら)、その添字を用いて、生徒名の配列[添字]に名前を、点数の配列[添字]に点数を格納
 ※ なくて生徒名がnullになってなければ満員なので、その旨を表示する

作成例

//p.222 練習問題2
using System;
class Exam {
    string[] name; //インデクサで扱う名前の配列
    int[] point; //インデクサで扱う点数の配列
    int num = 0; //生徒数
    public int this[string s] { //名前をインデクスとして点数を扱うインデクサ
        get { 
            for (int i = 0; i < num; i++) { //全生徒について繰返す
                if(name[i] == s) { //名前が一致?
                    return point[i]; //その点数を返す
                } else if (name[i] == null) { //空?
                    break; //繰返し終了
                }
            }
            return -1; //名前が一致しなければ-1を返す
        }
        set {
            for (int i = 0; i < num; i++) { //全生徒について繰返す
                if (name[i] == null) { //一致せず空きが有る?
                    name[i] = s; //名前を格納
                    point[i] = value; //その点数を格納
                    return; //インデクサを抜ける
                } else if(name[i] == s) { //名前が一致?
                    point[i] = value; //その点数を更新
                    return; //インデクサを抜ける
                }
            }
            Console.WriteLine("満員!");
        }
    }
    public Exam(int num) { //生徒数を受け取るコンストラクタ
        this.num = num;
        name = new string[num]; //名前の配列を生成
        point = new int[num]; //点数の配列を生成
    }
    public void show() {
        for (int i = 0; i < num; i++) {
            if (name[i] != null) { //空でなければ
                Console.Write("{0}:{1}, ", name[i], point[i]);
            }
        }
        Console.WriteLine(); //改行
    }
}
class indexer05 {
    public static void Main() {
        Exam e = new Exam(4);
        e["amuro"] = 60;
        e.show(); //「amuro:60,」
        e["shar"] = 70;
        e.show(); //「amuro:60, shar:70,」 
        e["amuro"] = 80;
        e.show(); //「amuro:80, shar:70,」
        e["bright"] = 90;
        e.show(); //「amuro:80, shar:70, bright:90,」
        e["ryu"] = 50;
        e.show(); //「amuro:80, shar:70, bright:90, ryu:50,」
        e["kai"] = 40; //「満員!」
        e.show(); //「amuro:60, shar:70, bright:90, ryu:50,」
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です