ノートの端の書き残し

UnityやらC#やら触っていたときのメモ

【Unity】transform.positionのメンバ変数を直接変更できない理由

小ネタです。(言語をちゃんと理解してる人には当たり前の話です。)

transform.position.x = 1f;

Unityを使い始めの頃、だれしもこんなコードを書いたことと思います。 そしてエディタに怒られたことでしょう。
f:id:u_osusi:20190917224101p:plain
これを解決するために、

var newVec = new Vector3(1f, 0, 0);
transform.position = newVec;

わざわざVector3をnewして代入する方法を取ったのではないかと思います。 (SetメソッドというのがVector3にはあるのですが、positionに使うのは罠です。なぜ罠かは後述します。)

解決方法はこれでいいとしてそもそもなぜ

transform.position.x = 1f;

は許されなかったのか。

理由はエディタが教えてくれているように、transform.positionが変数ではなくプロパティだから。
プロパティということは実際にはその裏にある変数を

public Vector3 get()
{
    return _position;
}

こんな感じで返しているのと同じです。
さて、ここで重要なのは、Vector3がstructであるということ。
structをreturnする際、実際には、returnの直後に書かれているstructではなくそのコピーが返されています。
f:id:u_osusi:20190917225855p:plain
このように、

transform.position.x = 1f;

はもしコンパイルエラーが起こらなかったとしても、本当に変更したい値ではなく、一瞬で消え去るそのコピーを変更することになっているわけです。 こういう無駄なことをさせないためにコンパイルエラーにしてくれているというわけですね。

さて、Vector3にはSetメソッドがあり、

github.com

transform.position.Set(1f, 0, 0);

のように書くことができます。これはコンパイル通ります。
が、実際には1fを代入していた場合と同様、コピーを変更しているだけなので、本当に変更したいpositionはなんの変更も受けません。

もちろんちゃんと勉強した人は値型と参照型の違いなんて知っているんですが、知っていても書いてるときに気付けないのがstructの怖いところ。
ただやはりstructは細かいところでパフォーマンス向上に貢献してくれます。 最近ではreadonlyを付けるなど、危険性を無くした安全なstructの使用が可能になってきているので、是非そういった機能を活用しましょう。

ufcpp.net