講義メモ

テキスト篇:p.369「コレクションクラスの初期化」から
ゲーム開発演習:演習24、自機の表示と左右移動、自機のアニメーション など

p.369 コレクションクラスの初期化

・ジェネリッククラスであるかどうかとは無関係に、コレクション初期化子によるコレクションクラスの初期化が可能
 ※ テキストではジェネリックなコレクションクラスのみを掲載しているが…
・書式: クラス名 参照変数 = new クラス名{値,…};
・書式(ジェネリッククラスの場合): クラス名<型> 参照変数 = new クラス名<型>{値,…};
・例:
 ArrayList anames = new ArrayList { "一郎", "二郎", "三郎" };
 List<string> names = new List<string> { "一郎", "二郎", "三郎" };

p.369 list02.cs

//p.369 list02.cs
using System;
using System.Collections.Generic; //List<T>用
class list02 {
    public static void Main() {
        List<string> names = new List<string> { "一郎", "二郎", "三郎" }; //コレクション初期化子
        foreach (string s in names) { //全要素について繰返す
            Console.WriteLine(s);
        }
    }
}

アレンジ演習:p.369 list02.cs

・非ジェネリッククラスであるArrayListに置き換えよう

作成例

//アレンジ演習:p.369 list02.cs
using System;
using System.Collections; //ArrayList用
class list02 {
    public static void Main() {
        ArrayList anames = new ArrayList { "一郎", "二郎", "三郎" }; //コレクション初期化子
        foreach (var s in anames) { //全要素について繰返す
            Console.WriteLine(s);
        }
    }
}

p.370(オブジェクト初期化子)

・p.370 list03.csでは(特に説明がないが)オブジェクト初期化子を利用している
・オブジェクト初期化子は「データメンバ名 = 値,…」形式でオブジェクトの初期値を設定できる記述法
・ジェネリッククラスであるかどうかとは無関係
・書式: クラス名 参照変数 = new クラス名{ データメンバ名 = 値,…};
・例:
 class BOOK { public string title; public string author; public decimal price; }
  :
 BOOK b = new BOOK {title = "我が輩は猫である", author = "夏目漱石", price = 1050};
・なお、この形式を用いるには、データメンバがpublicであること(プロパティによる代用は可能)
・p.370 list03.csのように、オブジェクト初期化子で初期化したオブジェクトを、コレクション初期化子に渡すことも可能

p.370 list03.cs

//p.370 list03.cs
using System;
using System.Collections.Generic;
class BOOK {
    public string title;
    public string author;
    public decimal price;
}
class list03 {
    public static void Main() {
        List<BOOK> mybook = new List<BOOK> { //コレクション初期化子
            new BOOK {title = "我が輩は猫である",
                author = "夏目漱石",
                price = 1050}, //オブジェクト初期化子
            new BOOK {title = "雲の階段",
                author = "渡辺淳一",
                price = 1600}, //オブジェクト初期化子
            new BOOK {title = "こころ",
                author = "夏目漱石",
                price = 1200} //オブジェクト初期化子
        };
        Console.WriteLine("----蔵書一覧----");
        foreach (BOOK b in mybook) { //全要素について繰返す
             Console.WriteLine("{0}, {1}, {2}円", b.title, b.author, b.price);
        }
    }
}

アレンジ演習:p.370 list03.cs

・オブジェクト初期化子で予め初期化したオブジェクトをコレクション初期化子に渡すように書き換えよう

作成例

//アレンジ演習:p.370 list03.cs
using System;
using System.Collections.Generic;
class BOOK {
    public string title;
    public string author;
    public decimal price;
}
class list03 {
    public static void Main() {
        BOOK b1 = new BOOK {title = "我が輩は猫である", author = "夏目漱石", price = 1050}; //オブジェクト初期化子
        BOOK b2 = new BOOK {title = "雲の階段", author = "渡辺淳一", price = 1600}; //オブジェクト初期化子
        BOOK b3 = new BOOK {title = "こころ", author = "夏目漱石", price = 1200}; //オブジェクト初期化子
        List<BOOK> mybook = new List<BOOK> { b1, b2, b3 }; //コレクション初期化子
        Console.WriteLine("----蔵書一覧----");
        foreach (BOOK b in mybook) { //全要素について繰返す
             Console.WriteLine("{0}, {1}, {2}円", b.title, b.author, b.price);
        }
    }
}

p.371 ジェネリッククラスと継承

・ジェネリッククラスの継承には、型パラメータの指定方法による制限がある
・ジェネリッククラスの型パラメータに具体的な型を指定している状態にして、継承の基本クラスにできる
・書式: class 派生クラス : 基本ジェネリッククラス<型> {…}
・これをクローズ構築型といい、派生クラスは通常クラスでもジェネリッククラスでも良い
 例: class Slime : Monster {…}
・ジェネリッククラスの型パラメータを型パラメータのままにして、継承の基本クラスにできる
・書式: class 派生クラス<型パラメータ> : 基本ジェネリッククラス<型パラメータ> {…}
・これをオープン構築型といい、派生クラスもジェネリッククラスにする
・これにより、型パラメータの引継ぎが行われる

p.371 generic03.cs

//p.371 generic03.cs
using System;
class MyClass<T> { //基本ジェネリッククラス
    public T x;
}
class MyClass2 : MyClass<int> { //クローズ構築型を継承
    //ここに public int x; があるとみなされる(ジェネリックは解決されている)
    public void show() {
        Console.WriteLine(x);
    }
}
class generic03 {
    public static void Main() {
        MyClass2 mc = new MyClass2();
        mc.x = 10; //クローズ構築型で指定したint型になっている
        mc.show();
    }
}

アレンジ演習:p.371 generic03.cs

・クローズ構築型では派生クラスは通常クラスでもジェネリッククラスでも良い
・MyClass2クラスをジェネリッククラスMyClass2<T>クラスにして、T型のデータメンバyを追記して動作を確認しよう
・「class MyClass<T> {…}」「class MyClass2<T> : MyClass<int> {…}」となるが、型パラメータは引継がれないので、
 2つのTは別のものとして扱われる

作成例

//アレンジ演習:p.371 generic03.cs
using System;
class MyClass<T> { //基本ジェネリッククラス
    public T x;
}
class MyClass2<T> : MyClass<int> { //クローズ構築型を継承するジェネリッククラス(型パラメータは引継がれない)
    //ここに public int x; があるとみなされる(ジェネリックは解決されている)
    public T y;
    public void show() {
        Console.WriteLine("x = {0} y = {1}", x, y);
    }
}
class generic03 {
    public static void Main() {
        MyClass2<string> mc = new MyClass2<string>(); //派生ジェネリッククラスのオブジェクト生成
        mc.x = 10; //クローズ構築型で指定したint型になっている
        mc.y = "Shar"; //ジェネリッククラスのオブジェクト生成で指定したstring型になっている
        mc.show();
    }
}

p.372 generic04.cs

//p.372 generic04.cs
using System;
class MyClass<T> { //基本ジェネリッククラス
    public T x;
}
class MyClass2<T> : MyClass<T> { //オープン構築型を継承 
    //ここに public T x; があるとみなされる(指定した型になる)
    public void show() {
        Console.WriteLine(x);
    }
}
class generic04 {
    public static void Main() {
        MyClass2<int> mc = new MyClass2<int>(); //定義のすべてのTがintになる
        mc.x = 2;
        mc.show();
        MyClass2<string> mc2 = new MyClass2<string>(); //定義のすべてのTがstringになる
        mc2.x = "test";
        mc2.show();
    }
}

アレンジ演習:p.372 generic04.cs

・MyClass2クラスをジェネリッククラスMyClass2<T,U>クラスにして、U型のデータメンバyを追記して動作を確認しよう
・「class MyClass<T> {…}」「class MyClass2<T,U> : MyClass<T> {…}」となり、型パラメータTは引継がれ、
 2つのTは同じものとして扱われる

作成例

//アレンジ演習:p.372 generic04.cs
using System;
class MyClass<T> { //基本ジェネリッククラス
    public T x;
}
class MyClass2<T, U> : MyClass<T> { //オープン構築型を継承 
    //ここに public T x; があるとみなされる(指定した型になる)
    public U y;
    public void show() {
        Console.WriteLine("x = {0} y = {1}", x, y);
    }
}
class generic04 {
    public static void Main() {
        MyClass2<int, string> mc = new MyClass2<int, string>(); //定義のすべてのTがintになる
        mc.x = 2; //型パラメータT経由でint型になる
        mc.y = "test"; //型パラメータU経由でstring型になる
        mc.show();
    }
}

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

・複数の型パラメータを持つジェネリッククラスを継承するとき、全て型パラメータに型を指定すれば、クローズ構築型にすることができる
・あるいは、同じ並びの型パラメータを持つジェネリッククラスで継承すれば、オープン構築型になる
・また、一部の型パラメータに型を指定することも可能で、こうするとオープン構築型になる
例: class Slime<T> : Monster<T, int> {…}

p.373 generic05.cs

//p.373 generic05.cs
using System;
class MyClass<T, U> { //2つの型パラメータを持つ基本ジェネリッククラス
    public T x;
    public U y;
}
class MyClass2<T> : MyClass<T, int> { //共有されていない型は明示するオープン構築型
    // ここに「public T x;」があるとみなされる
    // ここに「public int y;」があるとみなされる
    public void show() {
        Console.WriteLine("x = {0}, y = {1}", x, y);
    }
}
class generic05 {
    public static void Main() {
        MyClass2<string> mc = new MyClass2<string>(); //Tの型のみ指定すれば良い
        mc.x = "cat"; //生成時に指定したstringがTになる
        mc.y = 20; //継承時に指定したintがUになる
        mc.show();
    }
}

p.374 型パラメータの制約

・ジェネリッククラスの定義において「どんな型が指定されてもOK」ということはあまりない
・データメンバと単純なプロパティのみを持つクラスであれば可能だが、なんらかのメソッドを持つ場合、
 指定できる型を制限する必要がある

・型パラメータに指定できるクラスを制約として明示することができる
・書式: class ジェネリッククラス<型パラメータ> where 型パラメータ : クラス名 {…}
・こうすると、指定したクラス及びその派生クラスのみ型パラメータに指定できる
・例: class SlimeHouse<T> where T : Slime {…}
 ⇒ SlimeHouse<HoimiSlime> m; //指定可能
・よって、指定したクラスに定義されているメソッドをジェネリッククラス内で記述可能になる

・制約として「struct」を指定すると、値型のみ型パラメータに指定できる
・例: class MyCalc<T> where T : struct {…}
 ⇒ MyCalc<int> m; //指定可能
・よって、値型であることが必要な記述が可能になる

・制約として「class」を指定すると、参照型のみ型パラメータに指定できる
・例: class MonsterHouse<T> where T : class {…}
 ⇒ MonsterHouse<Dragon> m; //指定可能
・よって、参照型であることが必要な記述が可能になる

・制約としてインターフェイス名を指定すると、指定したインターフェイスを実装したクラスのみ型パラメータに指定できる
・よって、指定したインターフェイスに定義されているメソッドをジェネリッククラス内で記述可能になる

・制約として「class, new()」を指定すると、デフォルトコンストラクタを持つクラスのみ型パラメータに指定できる
・制約として「クラス名, new()」を指定すると、指定したクラス及びその派生クラスでデフォルトコンストラクタを持つもののみ
 型パラメータに指定できる
・例: class SlimeHouse<T> where T : Slime, new() {…}
 ⇒ SlimeHouse<HoimiSlime> m; //HoimiSlimeクラスにデフォルトコンストラクタがあれば指定可能
・よって、デフォルトコンストラクタを呼び出す処理をジェネリッククラス内で記述可能になる

p.375 generic06.cs

//p.375 generic06.cs
using System;
class MyClass {
    public int x;
    public string name;
    public MyClass() { //デフォルトコンストラクタ
        x = 0;
        name = "";
    }
}
//ジェネリッククラスの定義(Tの制約:MyClassまたはその派生クラスでデフォルトコンストラクタ有)
class MyClass2<T> where T : MyClass, new() { 
    T p = new T(); //デフォルトコンストラクタがある前提なのでOK
    public void show() {
        //MyClassまたはその派生クラスなのでxとnameがあるからOK
        Console.WriteLine("x = {0}, p.name = {1}", p.x, p.name); 
    }
    public void setxname(int n, string str) {
        p.x = n; //MyClassまたはその派生クラスなのでint型のxがあるからOK
        p.name = str; //MyClassまたはその派生クラスなのでstring型のnameがあるからOK
    }
}
class generic06 {
    public static void Main() {
        MyClass2<MyClass> mc2 = new MyClass2<MyClass>(); //制約に反しないのでOK
        mc2.setxname(100, "abc");
        mc2.show();
    }
}

アレンジ演習:p.375 generic06.cs

・MyClassの派生クラスYourClassを定義し「MyClass2<YourClass>」で利用可能なことを確認しよう

作成例

//アレンジ演習:p.375 generic06.cs
using System;
class MyClass {
    public int x;
    public string name;
    public MyClass() { //デフォルトコンストラクタ
        x = 0;
        name = "";
    }
}
class YourClass : MyClass { //【以下追加】MyClassの派生クラスの定義
    //ここに「public int x;」があるとみなされる
    //ここに「public string name;」があるとみなされる
    public int y;
    public YourClass() { //デフォルトコンストラクタ
        x = 0;
        y = 10;
        name = "";
    }
}
//ジェネリッククラスの定義(Tの制約:MyClassまたはその派生クラスでデフォルトコンストラクタ有)
class MyClass2<T> where T : MyClass, new() { 
    T p = new T(); //デフォルトコンストラクタがある前提なのでOK
    public void show() {
        //MyClassまたはその派生クラスなのでxとnameがあるからOK
        Console.WriteLine("x = {0}, p.name = {1}", p.x, p.name); //※派生クラスのyは利用不可
    }
    public void setxname(int n, string str) {
        p.x = n; //MyClassまたはその派生クラスなのでint型のxがあるからOK
        p.name = str; //MyClassまたはその派生クラスなのでstring型のnameがあるからOK
    }
}
class generic06 {
    public static void Main() {
        MyClass2<YourClass> mc2 = new MyClass2<YourClass>(); //【変更】制約に反しないのでOK
        mc2.setxname(100, "abc");
        mc2.show();
    }
}

コメントを残す

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