講義メモ

テキスト篇:p.363「ジェネリックとは」から
ゲーム開発演習:背景画面のスクロール、キーボードの状態を知る など

p.363 ジェネリックとは

・ジェネリックとは「総称」という意味で「型を変数で扱えば、型だけが異なるクラスやメソッドを1つにできる」仕組み
・つまり、メソッドのオーバーロードや引数の可変個化(p.201)などをさらに進化させた位置づけ

p.363 ジェネリッククラス

・利用時に型を指定できるクラス
・定義書式: class クラス名<型パラメータ> {…}
・通常、型パラメータには大文字1文字を用いることが多い(C#が提供するジェネリッククラスがそうなので)
・例: class Monster<T> { public T power; } //型パラメータで指定した型のデータメンバを持つクラス
・ジェネリッククラスを通常のクラスから利用するには、宣言時と生成時に型を指定する
・宣言書式: クラス名<型> 参照変数;
・生成書式: 参照変数 = new クラス名<型>(…);
・宣言と生成を同時に行う場合、型を2箇所指定できる: クラス名<型> 参照変数 = new クラス名<型>(…);
・例: Monster<int> rimuru = new Monster<int>(); //①powerを整数で扱うモンスターrimuruの初期化
・例: Monster<double> gobuta = new Monster<double>(); //②powerを実数で扱うモンスターgobutaの初期化
・例: Monster<string> shuna = new Monster<string>(); //③powerを文字列で扱うモンスターshunaの初期化
・つまり、内部的に型パラメータの置き換えが行われ、その結果のオブジェクトが生成される
・例: class Monster<T> { public T power; } ⇒ class Monster { public int power; } //①の場合

p.364 generic01.cs

//p.364 generic01.cs
using System;
class MyClass<T> { //型パラメータTを持つジェネリッククラス
    public T name; //宣言時に型が決まるデータメンバ
    public T GetVal() { //宣言時戻り値が決まるメソッド(アクセッサ)
        return name; //nameの型が戻り値型に一致するのでどんな型でもOK
    }
}
class generic01 {
    public static void Main() {
        MyClass<int> mca = new MyClass<int>(); //型にintを指定してインスタンスを生成
        mca.name = 10; //インスタンス変数nameの型はintなので整数を代入できる
        Console.WriteLine(mca.GetVal()); //GetValメソッドの戻り値型はintなので整数が返る
        MyClass<string> mcb = new MyClass<string>(); //型にstringを指定してインスタンスを生成
        mcb.name = "猫"; //インスタンス変数nameの型はstringなので文字列を代入できる
        Console.WriteLine(mcb.GetVal()); //GetValメソッドの戻り値型はstringなので文字列が返る
    }
}

アレンジ演習:p.364 generic01.cs

・mca.GetVal()は戻り値型がintなので、これに+10すると加算になる
・mcb.GetVal()は戻り値型がstringなので、これに+10すると連結になる
・以上を追記して確認しよう

作成例

//アレンジ演習:p.364 generic01.cs
using System;
class MyClass<T> { //型パラメータTを持つジェネリッククラス
    public T name; //宣言時に型が決まるデータメンバ
    public T GetVal() { //宣言時戻り値が決まるメソッド(アクセッサ)
        return name; //nameの型が戻り値型に一致するのでどんな型でもOK
    }
}
class generic01 {
    public static void Main() {
        MyClass<int> mca = new MyClass<int>(); //型にintを指定してインスタンスを生成
        mca.name = 10; //インスタンス変数nameの型はintなので整数を代入できる
        Console.WriteLine(mca.GetVal() + 10); //GetValメソッドの戻り値型はintなので整数が返り加算
        MyClass<string> mcb = new MyClass<string>(); //型にstringを指定してインスタンスを生成
        mcb.name = "猫"; //インスタンス変数nameの型はstringなので文字列を代入できる
        Console.WriteLine(mcb.GetVal() + 10); //GetValメソッドの戻り値型はstringなので文字列が返り連結
    }
}

p.365(複数の型パラメータを持つジェネリッククラス)

・ジェネリッククラスの型パラメータ数には制限はない
・2つ以上を用いる場合は、カンマで区切る
・定義書式例: class クラス名<型パラメータ①, 型パラメータ②> {…}
・例: class Monster<T, U> { public T hp; public U mp; } //型パラメータで指定した型のデータメンバを持つクラス
・複数の型パラメータを持つジェネリッククラスを通常のクラスから利用するには、宣言時と生成時に全ての型パラメータに型を指定する
・宣言書式: クラス名<型①,型②> 参照変数;
・生成書式: 参照変数 = new クラス名<型①,型②>(…);
・宣言と生成を同時に行う場合: クラス名<型①,型②> 参照変数 = new クラス名<型①,型②>(…);
・例: Monster<int, double> rimuru = new Monster<int, double>(); //hpを整数、mpを実数で扱うモンスターrimuruの初期化

p.365(配列の型パラメータを持つジェネリッククラス)

・データメンバとして配列を持ち、その型をジェネリックで指定することも可能
・宣言書式: 型パラメータ[] 配列名;
・生成書式: 配列名 = new 型パラメータ[要素数];
・型が宣言時に決まらないので、初期化はできないが、要素数指定の宣言+生成は可能
 例: public T[] a = new T[10];

p.365 generic02.cs

//p.365 generic02.cs
using System;
class MyClass<T, U> { //2個の型パラメータT,Uを持つジェネリッククラス
    public T[] x; //T型の配列xの宣言のみ
    public U[] y; //U型の配列yの宣言のみ
    public MyClass(int n) { //コンストラクタ(引数は要素数)
        x = new T[n]; //要素数nでT型の配列xの要素を生成
        y = new U[n]; //要素数nでU型の配列yの要素を生成
    }
}
class generic02 {
    public static void Main() {
        int n; //配列の要素数       
        Console.Write("n = ");
        string strN = Console.ReadLine();
        if (!Char.IsDigit(strN[0])) { //先頭文字が数字ではない?
            Console.WriteLine("入力が不適切です");
            return; //終了
        }
        n = int.Parse(strN); //整数化
        //2引数を指定してインスタンス生成、コンストラクタが動作し2配列の要素を各n個生成
        MyClass<int, string> mc = new MyClass<int, string>(n); 
        for (int i = 0; i < n; i++) { //全要素について繰返す
            Console.Write("番号--- ");
            string strNo = Console.ReadLine();
            if (!Char.IsDigit(strNo[0])) { //先頭文字が数字ではない?
                Console.WriteLine("不適切な番号です");
                break; //終了
            }
            mc.x[i] = int.Parse(strNo); //整数化して配列xの要素[i]に代入
            Console.Write("氏名--- ");
            string strName = Console.ReadLine();
            mc.y[i] = strName; //そのまま配列yの要素[i]に代入
        }
        Console.WriteLine(); //改行
        for (int i = 0; i < n; i++) { //全要素について繰返す
            Console.WriteLine("[{0}] {1}", mc.x[i], mc.y[i]);
        }
    }
}

【補足】ジェネリックプロパティ

・プロパティの戻り値型を型パラメータにできる
・定義書式: public 型パラメータ プロパティ名 { get {…} set {…} }
・例: public T HP { get { return hp; } set { hp = value; } }
・valueの内部的な型は、自動的に型パラメータと同じ型になる

アレンジ演習:p.364 generic01.cs

・GatValメソッドをプロパティ(getのみ)にしよう

作成例

//アレンジ演習:p.364 generic01.cs
using System;
class MyClass<T> { //型パラメータTを持つジェネリッククラス
    public T name; //宣言時に型が決まるデータメンバ
    public T GetVal { //宣言時戻り値が決まるプロパティ
        get { return name; } //nameの型が戻り値型に一致するのでどんな型でもOK
    }
}
class generic01 {
    public static void Main() {
        MyClass<int> mca = new MyClass<int>(); //型にintを指定してインスタンスを生成
        mca.name = 10; //インスタンス変数nameの型はintなので整数を代入できる
        Console.WriteLine(mca.GetVal); //GetValプロパティの戻り値型はintなので整数が返る
        MyClass<string> mcb = new MyClass<string>(); //型にstringを指定してインスタンスを生成
        mcb.name = "猫"; //インスタンス変数nameの型はstringなので文字列を代入できる
        Console.WriteLine(mcb.GetVal); //GetValプロパティの戻り値型はstringなので文字列が返る
    }
}

【補足】ジェネリックインデクサ

・インデクサの型を型パラメータにできる
・定義書式: public 型パラメータ this[型 インデックス] { get {…} set {…} }
・例: public T this[int i] { get { return a[i]; } set { a[i] = value; } }

アレンジ演習:p.365 generic02.cs

・配列xをジェネリックインデクサで扱うようにしよう

作成例

//アレンジ演習:p.365 generic02.cs
using System;
class MyClass<T, U> { //2個の型パラメータT,Uを持つジェネリッククラス
    public T[] x; //T型の配列xの宣言のみ
    public U[] y; //U型の配列yの宣言のみ
    public MyClass(int n) { //コンストラクタ(引数は要素数)
        x = new T[n]; //要素数nでT型の配列xの要素を生成
        y = new U[n]; //要素数nでU型の配列yの要素を生成
    }
    public T this[int i] { //【以下追加】T型の配列xによるインデクサ
        get { return x[i]; } set { x[i] = value; }
    }
}
class generic02 {
    public static void Main() {
        int n; //配列の要素数       
        Console.Write("n = ");
        string strN = Console.ReadLine();
        if (!Char.IsDigit(strN[0])) { //先頭文字が数字ではない?
            Console.WriteLine("入力が不適切です");
            return; //終了
        }
        n = int.Parse(strN); //整数化
        //2引数を指定してインスタンス生成、コンストラクタが動作し2配列の要素を各n個生成
        MyClass<int, string> mc = new MyClass<int, string>(n); 
        for (int i = 0; i < n; i++) { //全要素について繰返す
            Console.Write("番号--- ");
            string strNo = Console.ReadLine();
            if (!Char.IsDigit(strNo[0])) { //先頭文字が数字ではない?
                Console.WriteLine("不適切な番号です");
                break; //終了
            }
            mc[i] = int.Parse(strNo); //【変更】整数化して配列xの要素[i]にインデクサ経由で代入
            Console.Write("氏名--- ");
            string strName = Console.ReadLine();
            mc.y[i] = strName; //そのまま配列yの要素[i]に代入
        }
        Console.WriteLine(); //改行
        for (int i = 0; i < n; i++) { //全要素について繰返す
            Console.WriteLine("[{0}] {1}", mc[i], mc.y[i]); //【変更】配列xはインデクサ経由で利用
        }
    }
}

p.367(ジェネリックとコレクションクラス)

・ArrayListクラスのようにデータ構造を提供するクラスの中には、ジェネリッククラスもあり、その代表例がList<T>クラス
・List<T>クラスはデータ構造に格納したい要素の型を型パラメータで与えて用いる
・すると、配列と同様に使えて配列より柔軟なデータ構造を型指定で利用できる
・ArrayListクラスでは格納すると型情報が失われ、取り出し時にキャストで与える必要があるが、
 List<T>クラスでは型情報を与えられるのでその必要はなく、ミスを防止できる
・List<T>クラスにも、要素を格納するAddメソッド、要素数を返すCountプロパティ、整列するSortメソッドなどがある
・using System.Collections.Generic; を指定すると良い

p.368 list01.cs

// p.368 list01.cs
using System;
using System.Collections.Generic; //List用
class list01 {
    public static void Main() {
        bool bEnd = false; //終了フラグをオフに
        List<int> mylist = new List<int>(); //int型を指定してジェネリックコレクションクラスを利用
        while (true) { //無限ループ
            Console.Write("Data = ");
            string strData = Console.ReadLine();
            if (!Char.IsDigit(strData[0])) { //先頭文字が数字ではない?
                bEnd = true; //終了フラグをオンに
            } else {
                mylist.Add(int.Parse(strData)); //整数化してリストに格納(いくつでもOK)
            }
            if (bEnd) { //終了フラグ:オン?
                break; //繰返し終了
            }
        }
        Console.WriteLine(); //改行
        for (int i = 0; i < mylist.Count; i++) { //全要素について繰返す
            Console.WriteLine("[{0}] {1}", i, mylist[i]);
        }
        Console.WriteLine();
        mylist.Sort(); //昇順に整列
        for (int i = 0; i < mylist.Count; i++) { //全要素について繰返す
            Console.WriteLine("[{0}] {1}", i, mylist[i]);
        }        
    }
}

コメントを残す

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