C#の値型と参照型について【Unity初心者入門】

ゲームプログラミング入門【値型と参照型】【Unity初心者入門講座】【ゲームの作り方】#24

前回は、インスタンスについて解説してきました。今回は、型の種類について解説していきます。

↑の動画でも解説していますので、是非ご活用ください。

この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。

<型の種類>

さて、インスタンスを取り扱うにあたって、ちょっと疑問に思った方もいらっしゃるかもしれません。

インスタンスの解説については↓の記事を参考にしてください。

今までの解説で、GetComponentとかGameObject.Findとかインスタンスを取得する命令を使った際、捕まえたインスタンスを変数に入れていました。

シーン上で既にインスタンス化(実体化)しているにも関わらず、それが入る変数(箱)を用意しています。 箱を用意するということはその分だけのメモリを確保するということになります。

シーン上でもメモリを使い、スクリプト上でもメモリを使い、食いすぎじゃないのか?ということですね。

安心してください。

型には種類があり、型によってデータの取り扱い方が違います。

この種類には値型と参照型があります。

<値型とは>

値型というのは呼んで字のごとく値をもつ型の事です。

intとかfloatとかですね。絶対に箱の中に実体そのものが入っています。まぁ、今までのイメージ通りのものだと思ってもらって大丈夫です。

<参照型とは>

参照型というのは、データを取り扱う際、インスタンスそのものではなく、インスタンスの場所を示したもの使用する型です。

要は箱の中に入っているのはインスタンスではなくインスタンスがある場所のメモと言えるかもしれません。メモに書いてあるのはインスタンスが存在するメモリのアドレスになります。

そのため、参照型の変数を複数用意したとしても、実体が複数存在するわけではなく、メモリも食いまくっていたわけではないことがわかります。

<値型と参照型で気を付ける事>

さて、メモリ管理はC#側がうまい具合にやってくれていることがわかりましたが、だから何だと思う方もいらっしゃるかと思います。

しかしながら、ちょっと気を付けないと痛い目を見ます。

例えば、

transform move

↑のようなスクリプトを組んだとします。単にTransformを入れた変数を入れ替えただけです。

これをCubeにくっつけて実行すると

move cube

普通に動いています。これはなんとなくわかりますよね

では今度はVector3で同じような事をしてみます。

vector3 move

Vector3の変数を用意して、単にTransformのPositionを入れているだけです。

こちらもその値をアップデートで足しています。

が、再生を押してもこちらは動きません

これはTransformは参照型で、Vector3は値型という違いから来ています。

参照型はインスタンスのメモリのアドレスを格納しているので、別の変数に渡したとしても、それが差すものは同一のインスタンスです。

しかし、値型はそれぞれが、値を持つ、つまり実データを持つという特徴があります。

値型の変数から変数に入れる際、それぞれが実データを持たなければならないため、代入する側の値をコピーして、変数に格納します。

つまり、Vector3での例の場合、代入した時点で「値は一緒だが別の物」となり、別の物の値をプラスしたところで、元々のTransformのPositionとは関係ないものになります。

そのため、Vector3の場合は右へ動かなかったわけです。

このように

・元データに反映させたいのに、値型をいじってしまっている。

・元データを変更したくないのに、参照型をいじってしまっている。

というのは、初心者の頃よくあるミスだと思うので覚えておきましょう。

特にメソッドで引数を渡す際は注意が必要ですね。

<何故型に種類があるのか>

さて、型には値型と参照型があるというのはわかっていただけたと思いますが、では何故、このように種類があるのでしょうか。挙動が違うのでミスを誘発しそうです。

実は、一見、いちいちコピーを作成せず、インスタンスが1つな参照型の方が優れているように見えますが、参照型は参照しているメモリの場所にアクセスするのに時間がかかります。

一方、値型は実データを保持しているため、アクセス時間は短いです。しかしながら、それぞれが値を持つので、いちいちコピーを作成する必要があります。

これらの点から、コピーする際に負荷があまりかからない軽いデータなら値型の方が早く、それ以外の場合は参照型の方が早くなります。

それぞれで利点が存在するため、このように型が分かれていたわけですね。

ただし、小さいデータであっても、何度もコピーを作成していては遅くなってしまうため、その場合は参照型の方が早い場合があります。

<何が値型で何が参照型か>

さて、今のままでは何が、値型で何が参照型なのかわからないと思うので、今までの解説で登場してきた型はどちらなのかを紹介したいと思います。

今まで登場してきた型は↓のような感じです。

値型

int
double
float
char
bool

構造体

参照型

string

class

配列

まだまだたくさん型がありますが、まだ解説していないものに関しては登場した際に解説しようと思います、

基本的な型はだいたい値型です。

stringだけ参照型なのですが、挙動が値型と同じという特別な型になります。そのため、stringは参照型だが参照型とは使い方が違うという不思議な感じになります。

そのため、stringについては、参照型というより、値型モドキと覚えておいた方がいいかもしれません。

また、classは参照型になります。Transformはコレですね。

構造体というのはclassと非常によく似た型で、設計図から実体を作成するイメージという点はclassと一緒なのですが、大きな違いとして、こちらは値型になります。

他にも細かい違いがあるのですが、ここでは割愛します。

Vector3は構造体ですので、値型だったわけです。

クラスと構造体の見分け方はスクリプトで書いてみたらわかると思います。

Transform

class

Vector3

struct

ちょっと小さくてすいませんが、Transformの方はclassと書かれているのに対して、Vector3はstructと書かれています。

このstructというのが構造体ですので、これで見分けていただければと思います。

そもそもクラスも構造体もいわゆる設計図ですので、プログラムを組んだ人によって内容が変わってきてしまう代物です。ですので、何がクラスで何が構造体だと決めつけることができません。

そのため、型ごとに見ていくしかないかと思います。

また、配列は、たとえ値型の配列であったとしても、参照型になりますので、注意しましょう。

<まとめ>

さて、いかがだったでしょうか。この値型と参照型がよくわかっていないと、データの動きがよくわからなくて、沼にはまってしまうかもしれないので、覚えておくといいかなと思います。

なんかデータが変な反映のされ方されているなと感じたら値型と参照型を間違えていないか確認してみてください

Point

・型には値型と参照型があるよ!
・値型は実体そのものが入っているよ!
・参照型は「実体がある場所」を記したメモが入っているよ!
・データの反映のされ方に注意しよう!


タイトルとURLをコピーしました