・文法編:ジェネリクス、ToStringメソッド など
・ゲーム開発編:特別編「3D迷路&シューティングゲーム開発演習」
文法編:ジェネリクス
・p.187の説明の通り、下記の構文の「<型名>」の構文をジェネリクスという Vegetable.Type typeA = GetComponent<Vegetable>().type; ・C言語などでは、同じ処理内容でも、用いるデータの型が異なる場合、その違いの数だけ名前の異なる関数を作成する必要がある ・これを改良する考え方がオーバーロードで、用いるデータの型が異なれば同じ名前の関数(メソッド)にできる ・さらに、改良して、用いるデータの型をパラメータとして与えられるようにしたのが、ジェネリクス(ジェネリックメソッド) ・GetComponentはUnityが提供するジェネリックメソッドであり、プログラマが自前で定義することも可能 ・用いるデータの型を受け渡すパラメータを型パラメータといい、大文字のTなどを用いることが多い
文法編:ジェネリックメソッドの例:同じ型の2引数を受け取って両方を出力するメソッド
・int型用は public void Log2(int a, int b) { Debug.Log(a); Debug.Log(b); }
・double型用は public void Log2(double a, double b) { Debug.Log(a); Debug.Log(b); }
・これをジェネリックメソッドにすると、型パラメータを引数型にも用いて下記のようにできる
public void Log2<T>(T a, T b) { Debug.Log(a); Debug.Log(b); }
・そして、呼び出しにおいて、型情報を渡すと、それに応じたメソッドが自動生成&利用される
Log2<int>(10, 20);
Log2<double>(3.14, 2.22);
演習 ex0914a.cs
・上記の例を試すプログラムを作成しよう
作成例
//演習 ex0914a.cs
using UnityEngine;
public class ex0914a : MonoBehaviour{
public void Log2<T>(T a, T b) { //ジェネリックメソッド(引数型がT)
Debug.Log(a); Debug.Log(b);
}
void Start() {
Log2<int>(10, 20); //型としてintを指定してジェネリックメソッドを呼ぶ
Log2<double>(3.14, 2.22); //型としてdoubleを指定してジェネリックメソッドを呼ぶ
}
}
文法編:ジェネリッククラス
・クラス定義そのものをジェネリックにして、クラス内で型パラメータを使いまわすことも可能
・これをジェネリッククラスという
・例:
class Nums<T> {
public void Log2(T a, T b) { //ジェネリックメソッド(引数型がT)
Debug.Log(a); Debug.Log(b);
}
}
・ジェネリッククラスはオブジェクトの生成時に型パラメータを渡すことができる
・例:
Nums<int> num = new Nums<int>();
演習 ex0914b.cs
・上記の例を試すプログラムを作成しよう
作成例
//演習 ex0914b.cs
using UnityEngine;
public class ex0914b : MonoBehaviour{
class Nums<T> { //ジェネリッククラス
public void Log2(T a, T b) { //メソッド(引数型がTで得られる)
Debug.Log(a); Debug.Log(b);
}
}
void Start() {
Nums<int> num = new Nums<int>(); //ジェネリッククラスに型を与えてオブジェクト生成
num.Log2(10, 20); //メソッドの引数型が決まる
Nums<char> cnum = new Nums<char>();
cnum.Log2('A', 'x');
}
}
文法編:C#が提供するジェネリックコレクションクラス
・コレクションクラスとはデータ構造を提供する汎用クラス ・例えば、配列の機能を拡張するクラスである ArrayListクラスがあり、配列の弱点である 「要素数が固定」「格納するデータの型が固定」「途中挿入や途中削除ができない(手間がかかる)」 などを解消している 参照: https://learn.microsoft.com/ja-jp/dotnet/api/system.collections.arraylist ・しかし、要素の型情報がない (格納と同時にObject型に変換されてしまう=型情報が失われるので、プログラム側で管理する必要がある)ため、 現在では、代わりに ジェネリックコレクションクラスList<T>を用いることが推奨されている ・利用方法 冒頭に「using System.Collections.Generic;」を挿入(Unityでは自動挿入されることもある) 生成: List<型> オブジェクト名 = new List<型>(); 格納: オブジェクト名.Add(要素値)メソッドで 要素数: オブジェクト名.Count プロパティで 要素値: オブジェクト名[添字] で配列と同様に 参照: https://learn.microsoft.com/ja-jp/dotnet/api/system.collections.generic.list-1
演習 ex0914c.cs
・Monsterクラスを定義し、中にpublicで文字列型のデータメンバnameを置く ・ジェネリックコレクションクラスList<Monster>のオブジェクトmonsを生成 ・適当なMonsterオブジェクトをいくつか生成しつつ、monsに格納する ・要素数を表示 ・全要素のnameを表示
作成例
using System.Collections.Generic;
using UnityEngine;
public class ex0914c : MonoBehaviour{
class Monster {
public string name;
public Monster(string name) { //コンストラクタ
this.name = name;
}
}
void Start() {
List<Monster> mons = new List<Monster>(); //ジェネリックコレクションを生成
mons.Add(new Monster("ヴェルドラ")); //Monsterを生成して格納
mons.Add(new Monster("リムル"));
mons.Add(new Monster("シュナ"));
Debug.Log("要素数:" + mons.Count); //プロパティで格納済要素数を得る
foreach (var m in mons) { //全要素について繰返す
Debug.Log("名前:" + m.name); //各要素のデータメンバを得て出力
}
}
}
文法編:ToStringメソッド
・C#のクラス構造の原則として「全クラスは自動的にObjectクラスの派生クラス扱い」 ・よって、Objectクラスのメンバが自動的に継承されている ・その一つがToStringメソッドで、内容はクラスを表す文字列になる 例: "ex0914c+Monster" ・C#では、自前のクラスの定義時に、ToStringメソッドをオーバーライドして、オブジェクトを表す文字列をreturnすることを推奨している ・なお、オブジェクトを文字列に連結したり、対応するメソッドに渡すと、自動的にToStringメソッドが呼び出される
演習 ex0914c.cs
・上記を試してみよう
作成例
using System.Collections.Generic;
using UnityEngine;
public class ex0914c : MonoBehaviour{
class Monster {
public string name;
public Monster(string name) { //コンストラクタ
this.name = name;
}
public override string ToString() { //自動継承したメソッドのオーバーライド
return "My name is " + this.name; //自オブジェクトを表す文字列を返す
}
}
void Start() {
List<Monster> mons = new List<Monster>(); //ジェネリックコレクションを生成
mons.Add(new Monster("ヴェルドラ")); //Monsterを生成して格納
mons.Add(new Monster("リムル"));
mons.Add(new Monster("シュナ"));
Debug.Log("要素数:" + mons.Count); //プロパティで格納済要素数を得る
foreach (var m in mons) { //全要素について繰返す
Debug.Log("名前:" + m); //各要素のToString()メソッドを自動的に呼ぶ
}
}
}