テキスト篇:p.377「ジェネリックメソッド」から
ゲーム開発演習:画像位置の管理、自弾の発射 など
p.374 型パラメータの制約(補足① struct)
・制約として「struct」を指定すると、値型のみ型パラメータに指定できる
・例: class MyCalc<T> where T : struct {…}
⇒ MyCalc<int> m; //指定可能
・よって、値型であることを条件にしたい場合に用いる
例:
using System;
//ジェネリッククラスの定義(Tの制約:値型のみ)
class MyClass<T> where T : struct {
T x;
public void show() {
Console.WriteLine("x = {0}", x);
}
public T X { set { x = value; } get { return x; } }
}
class generic06 {
public static void Main() {
MyClass<int> mc1 = new MyClass<int>(); //制約に反しないのでOK
mc1.X = 100;
mc1.show();
// MyClass<string> mc2 = new MyClass<string>(); //制約に反するのでエラー
}
}
p.374 型パラメータの制約(補足② struct)
・制約として「class」を指定すると、参照型のみ型パラメータに指定できる
・例: class MonsterHouse<T> where T : class {…}
⇒ MonsterHouse<Dragon> m; //指定可能
・よって、参照型であることを条件にしたい場合に用いる
例:
using System;
//ジェネリッククラスの定義(Tの制約:参照型のみ)
class MyClass<T> where T : class {
T x;
public void show() {
Console.WriteLine("x = {0}", x);
}
public T X { set { x = value; } get { return x; } }
}
class generic06 {
public static void Main() {
MyClass<string> mc1 = new MyClass<string>(); //制約に反しないのでOK
mc1.X = "S";
mc1.show();
//MyClass<int> mc2 = new MyClass<int>(); //制約に反するのでエラー
}
}
p.374 型パラメータの制約(補足③ インターフェイス)
・制約としてインターフェイス名を指定すると、指定したインターフェイスを実装したクラスのみ型パラメータに指定できる
・よって、指定したインターフェイスに定義されているメソッドをジェネリッククラス内で記述可能になる
例:
using System;
//ジェネリッククラスの定義(Tの制約:インターフェイス指定)
interface Flyable { //「飛べる」ことを示す
string howtofly(); //飛び方を返す
}
class Dragon : Flyable {
string name;
public Dragon() { name = "ドラゴン"; }
public string howtofly() { return "翼で飛ぶ"; }
}
class Robot : Flyable {
string name;
public Robot() { name = "ロボット"; }
public string howtofly() { return "ロケットで飛ぶ"; }
}
class Flyers<T> where T : Flyable, new() { //制約:インターフェイス名、デフォルトコンストラクタ有
T x = new T();
public void show() {
Console.WriteLine(x.howtofly()); //Flyableインターフェイス実装が確定なのでこのメソッドが呼べる
}
}
class generic06 {
public static void Main() {
Flyers<Dragon> mc1 = new Flyers<Dragon>(); //制約に反しないのでOK
mc1.show();
Flyers<Robot> mc2 = new Flyers<Robot>(); //制約に反しないのでOK
mc2.show();
//Flyers<int> mc3 = new Flyers<int>(); //制約に反するのでエラー
}
}
p.377 ジェネリックメソッド
・クラスと同様に、メソッド単体でも型パラメータの指定が可能で、これをジェネリックメソッドという
・書式: アクセス修飾子 戻り値型 メソッド名<型パラメータ,…>(引数リスト){…}
・インスタンスメソッドでも静的メソッドでも可能
・例:public static void MySet<T>(T x) { obj = (T)x; } //xはT型なのでキャスト可能
p.377 generic07.cs
//p.377 generic07.cs
using System;
class MyClass { //通常のクラス
static object obj; //静的変数
object ob; //インスタンス変数
public static void myset<T>(T x) { //静的ジェネリックメソッド
obj = (T)x; //型パラメータで受け取った型にキャストして代入
}
public static void show() { //静的メソッド
Console.WriteLine(obj.ToString());
}
public void myset2<T>(T x) { //インスタンスメソッドでジェネリックメソッド
ob = (T)x;//型パラメータで受け取った型にキャストして代入
}
public void show2() { //インスタンスメソッド
Console.WriteLine(ob.ToString());
}
}
class generic07 {
public static void Main() {
MyClass.myset<int>(12); //静的ジェネリックメソッドに型intを与えて呼ぶ
MyClass.show(); //静的メソッドを呼んで表示
MyClass.myset<string>("abc"); //静的ジェネリックメソッドに型intを与えて呼ぶ
MyClass.show(); //静的メソッドを呼んで表示
MyClass mc2 = new MyClass();
mc2.myset2<int>(100); //インスタンスメソッドでジェネリックメソッドに型intを与えて呼ぶ
mc2.show2(); //インスタンスメソッドを呼んで表示
}
}
補足:ジェネリックメソッドの戻り値型の型パラメータ
・ジェネリックメソッドでは内部や引数型のみならず、戻り値型にも型パラメータが指定できる
・例: public static List<T> makeList<T>(T x) {…}
・例えば、このメソッドは:
① T型のリストを生成
② 引数xで与えられたデータを①にAddメソッドで格納
③ ①を返す
ミニ演習: ジェネリックメソッドの戻り値型の型パラメータ mini377.cs
・上記の例を試すプログラムを作ろう
作成例
//ミニ演習: ジェネリックメソッドの戻り値型の型パラメータ mini377.cs
using System;
using System.Collections.Generic;
class MyClass { //通常のクラス
public static List<T> makeList<T>(T x) { //リストの型を受け取るジェネリックメソッド
List<T> mylist = new List<T>(); //受け取った型のリストを生成
mylist.Add(x); //T型なので無条件に格納可能
return mylist; //格納されたリストを返す
}
}
class mini377 {
public static void Main() {
List<double> test = MyClass.makeList<double>(3.14); //実数型リストを作って返すメソッド
Console.WriteLine(test[0]); //先頭要素を表示
}
}
p.378(ジェネリックメソッドと制約)
・ジェネリッククラスと同様の制約ジェネリクスメソッドにも指定できる
・書式:アクセス修飾子 戻り値型 メソッド名<型パラメータ,…>(引数リスト) where 型パラメータ:制約 {…}
・例:public List<T> makeList<T>(T x) : T:struct {…}
ミニ演習: mini377.cs・改
・ジェネリックメソッドの戻り値型の型パラメータのプログラム「mini377.cs」のジェネリクスメソッドに値型の制約を追記して 動作を確認しよう
作成例
//ミニ演習: ジェネリックメソッドの戻り値型の型パラメータ mini377.cs・改
using System;
using System.Collections.Generic;
class MyClass { //通常のクラス
public static List<T> makeList<T>(T x) where T:struct { //リストの型を受け取る制約付きジェネリックメソッド
List<T> mylist = new List<T>(); //受け取った型のリストを生成
mylist.Add(x); //T型なので無条件に格納可能
return mylist; //格納されたリストを返す
}
}
class mini377 {
public static void Main() {
List<double> test = MyClass.makeList<double>(3.14); //実数型リストを作って返すメソッド
Console.WriteLine(test[0]); //先頭要素を表示
//List<string> test1 = MyClass.makeList<string>("AB"); //制約のためにエラーになる
}
}
p.379 ジェネリックメソッドとオーバーロード
・型パラメータはシグニチャの一部になるので、型パラメータの個数が異なるジェネリックメソッドによるオーバーロードが可能
・例:
int Test(int){…}
int Test<T>(int){…}
int Test<T, U>(int){…}
・なお、戻り値型がシグニチャに含まれないのは、ジェネリックメソッドも同様
アレンジ演習:p.377 generic07.cs
・静的メンバを除外してから「public void myset2(T x, U y){…}」を追加して動作を確認しよう
作成例
//アレンジ演習:p.377 generic07.cs
using System;
class MyClass { //通常のクラス
static object obj; //静的変数
object ob; //インスタンス変数
object ob2; //インスタンス変数
public void myset2<T>(T x) { //インスタンスメソッドでジェネリックメソッド
ob = (T)x;//型パラメータで受け取った型にキャストして代入
}
public void myset2<T,U>(T x, U y) { //インスタンスメソッドでジェネリックメソッド
ob = (T)x;//型パラメータで受け取った型にキャストして代入
ob2 = (U)y;//型パラメータで受け取った型にキャストして代入
}
public void show2() { //インスタンスメソッド
Console.WriteLine(ob.ToString());
}
public void show2a() { //インスタンスメソッド
Console.WriteLine("{0} {1}", ob.ToString(), ob2.ToString());
}
}
class generic07 {
public static void Main() {
MyClass mc2 = new MyClass();
mc2.myset2<int>(100); //インスタンスメソッドでジェネリックメソッドに型intを与えて呼ぶ
mc2.show2(); //インスタンスメソッドを呼んで表示
mc2.myset2<int, double>(100, 3.14); //インスタンスメソッドでジェネリックメソッドに型intを与えて呼ぶ
mc2.show2a(); //インスタンスメソッドを呼んで表示
}
}
p.379 ジェネリックインターフェイス
・クラスと同様にインターフェイスにおいても型パラメータの指定が可能で、ジェネリックインターフェイスという
・書式: interface インターフェイス名<型パラメータ,…>{…}
・ジェネリックインターフェイスを実装するクラスにおいて型パラメータを与えて用いる
例:
using System;
//ジェネリッククラスの定義(Tの制約:インターフェイス指定)
interface Flyable<T> { //「飛べる」ことを示す
T howtofly(); //飛び方を返すメソッドを求めるが戻り値型は実装側で決めて良い
}
class Dragon : Flyable<string> { //string型に指定できる
string name;
public Dragon() { name = "ドラゴン"; }
public string howtofly() { return name + "は翼で飛ぶ"; } //string型に指定できる
}
class Robot : Flyable<int> { //intに指定できる
string name;
public Robot() { name = "ロボット"; }
public int howtofly() { return 3; } //intに指定できる
}
class generic06 {
public static void Main() {
Dragon Veldra = new Dragon();
Console.WriteLine(Veldra.howtofly());
Robot Atom = new Robot();
Console.WriteLine(Atom.howtofly());
}
}
p.380(プロパティの自動実装)
・p.381 anonymous02.csにおいて、プロパティの自動実装が用いられている
・内容が「get {return データメンバ;} set {データメンバ = value;}」のみのプロパティであれば、publicなデータメンバに続けて「{get; set;}」と記述することでプロパティの自動実装になる
・しかし、このプログラムにおいてはプロパティの自動実装を用いる必要性はなく、{get; set;}を記述しない場合と同じ結果になる
※ プロパティの自動実装はC#の簡易クラス機能などにおいて用いられる(今回は割愛)
※ また、C#の名前付けルールでは自動実装プロパティのプロパティ名の先頭文字は大文字と規定されている
※ なお、p.381 anonymous02.csの「int z;」は不用
//p.381 anonymous02.cs
using System;
class MyClass
{
public int x; //自動実装プロパティは必要ではない
}
class anonymous01
{
public static void Main()
{
MyClass mc = new MyClass();
mc.x = 10;
Console.WriteLine("x = {0}", mc.x);
}
}
p.380 匿名型
・varキーワードを用いて匿名型の参照変数を定義したとき、オブジェクト初期化子で生成したオブジェクトへの参照を代入できる
・こうすると、名前のないクラスが用意され、その中にオブジェクト初期化子で指定したメンバが定義され、初期値が与えられる
・このメンバの型は初期値により自動決定され、任意指定はできない
・書式: var 参照変数 = new {メンバ名 = 値, … };
・例: var v = new {a = 10, b = 20};
・なお、このメンバは「参照変数.メンバ名」で扱えるが読込専用で、初期値は変更できない
・複数の定数をグループ化する場合に便利
・例: var player = new {name = "Shar", age = 24, x = 10, y = 20};
p.381 anonymous03.cs
//p.381 anonymous03.cs
using System;
class anonymous03 {
public static void Main() {
var mc = new {x = 10}; //匿名型のオブジェクトを生成してメンバxに初期値を与える
Console.WriteLine("x = {0}", mc.x); //参照変数経由でメンバxの初期値を表示
}
}
※ p.382「ジェネリックの共変性・反変性」は割愛します
提出:アレンジ演習:p.377 generic07.cs