【C#】Listと配列の違いも意識して使いこなそう【ゲーム開発】
この記事で紹介したいのは複数の値のまとまりを扱うことができるList(リスト)と配列の機能です。
C#でリストを使う場合は特に「ジェネリックリスト」と呼ばれる型をパラメータとして渡すリストが便利です。
ちょっと細かい話ですが、同じように複数の値のまとまりを扱うことができる配列との違いもちょっと紹介します。
複数の値を持つためのListと配列
C#で複数の値のまとまりを扱う方法として、Listと配列があります。
List(リスト)とは
List(リスト)は同じ型の値を複数まとめて扱うことができる機能です。
上のList<int>のように宣言すればint型の値をまとめて保持しますし、List<GameObject>のように宣言すればGameObjectへの参照を複数保持することができます。
ここで使ったListはプログラミングする人がintやGameObjectのように型を自由に指定できることから、ジェネリックリストと呼ばれています。ジェネリック(Generic)とは「一般的である」とか「共通している」といった意味の言葉です。
型を指定しているので同じ型のデータが格納されているタイプセーフなリストになります。タイプセーフとは型が保証されていることを意味していて、List<int>だったらint型のデータが入っていることが保証されています。別の型のデータが入っていると、その型に存在しないメソッドを使ってしまってエラー、なんてことがあるので、このように中に入っているデータの型が一致していることが分かっていると安全なんです。
リストを使う場面ですが、例えば画面内に存在している敵キャラのオブジェクトをまとめて管理したい場合はリストで保持しておくと良いでしょう。リストならAddメソッドを使って項目の数を後から増やすこともできますからね。
foreachを使って各GameObjectにアクセスしていって、なんらかの操作を行うこともできます。
配列とは
配列も複数の値をまとめて扱うことができる機能です。
上のint[]のように宣言すればint型の値をまとめて保持しますし、GameObject[]のように宣言すればGameObjectへの参照を複数保持することができます。
値を複数まとめて保持できる点はListも配列も同じですが、配列の場合は宣言時に配列の大きさを宣言します。メモリ上で値を保持するための領域を確保する際には連続した番地を確保します。なので、事前に配列の大きさを知っておく必要があるんですね。
後から値を追加して配列を大きくする場合はAddのように元々の配列に追加するのではなく、newで新しく配列を作ることで領域を確保し直して値を保存します。なので、管理したいデータの数が変わるのであればListを、変わらないのであれば配列を使うといい感じです。
値はメモリ上に
プログラムがなんらかの値を保持する場合は、メモリ上に値を保存するための領域を確保します。例えば「int num = 5;」のように変数に値を代入すると、メモリ上で32ビット分の領域が確保されます。(C#のintは32ビットで値を表現しています)
配列は「int[] numArray = new int[3];」のように宣言され、同じ型の値を保持します。
同じ型の値をまとめて保持する点はリストと似ていますが、配列の場合は宣言されたタイミングで要素の数だけ領域を確保します。つまり、メモリ上で連続した番地を確保します。
リストの場合は宣言されたタイミングで領域を確保するのに加えて、Addした時などにも領域を確保しています。
実はC#では、ジェネリックリストは内部的には配列の形でデータを保持しています。ソースコードを確認すると、privateなフィールドとして「private T[] _items;」が宣言されていて、リスト(内部的には配列)の大きさが変更されるタイミングでnewして新しく配列を作っていました。
(以前は「リストはポインタで次の要素の場所を保持している」と思っていたのですが、これは(おそらく)C言語の場合とごっちゃになっていました。メルマガでリストの話をしたときにもこんなことを言っていたような気がするので、勘違いした情報を送ってしまってすみませんでしたorz)
Listと配列の使い分け
Listも結局内部的には配列を使っているわけですが、「じゃあどっちを使ったらいいのか?」というのはやはり気になるところです。
使い分けの判断ポイントは2つあって、
- 動的に要素数が変わるか
- パフォーマンス
の観点から選べばOKです。
もし動的に要素の数が変わるのであればList、変わらないのであれば配列を使います。事前に要素数が決まっているなら配列で大きさを指定できますからね。
また、配列の場合は領域の確保が1回で済むのに対し、ListはAddのタイミングでnewしてArray.Copy()が実行されています。これは値を追加するたびに大きさを変更したり配列の領域を確保し直しているので、多少パフォーマンスに影響がでます。
パフォーマンスに関しては目標とするFPSが出ているかで判断すると良いでしょう。目標FPSが達成できていて処理時間に余裕があればListを使い続ければ良いですし、処理時間を切り詰めたい場合は配列を使うようにするのも手です。この辺りはProfilerを見て確認するのが大切です。Profilerについては以下の記事もご参考に。
ここまで語っておいてアレですが、個人的にはListが使いやすいのでパフォーマンス調整の必要がない限りはほぼListを使っています。動的にリストの要素を増減できるのが便利すぎて手放せません٩(´꒳`)۶
まとめ
C#で複数の値のまとまりを扱う場合はListか配列を使用します。パフォーマンスを重視するなら配列を使用し、後から要素数を変更したい場合はListを使うと便利です。
今回ソースコードを覗いてみて、内部的にはListも配列で値を持っているのが個人的に勉強になりました(笑)
リストの型パラメータについてもう少し突っ込んで知りたい場合は以下の記事もご参照くださいな。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【C#】intやfloatは本名じゃない! C#のエイリアスの話【ゲーム開発】 2020.11.01
-
次の記事
【C#/Unity】初心者キラーのジェネリックについて知る【ゲーム開発】 2020.11.03
コメントを書く