【Unity/C#】const(コンパイル時定数)とreadonly(実行時定数)の違いを知る
UnityではC#を使ってコーディングしていきます。
プロジェクト内でスクリプトファイルを作成すると、テンプレートとしてMonoBehaviourを継承したクラスになっていますが、MonoBehaviourを継承しないクラスも使っていくと便利です。
例えばstaticなクラスとして、ゲーム内のどこからでも参照できるクラスを作成することもできます。ゲーム全体で使用する設定値などを保持しておくフィールドとして用意しておくことで、設定値を参照しやすくなります。その設定値を使うタイミングで値を定義して……とやっていると、設定値が変わったときに変更する範囲が多くなって辛くなりますからね。
こうした設定値を保持するにあたっては、constを使ったりreadonlyを使ったりする方法がありますが、この使い分けについて知っておくと、情報を調べる際に混乱せずにすみます。
const(コンパイル時定数)とreadonly(実行時定数)の違い
設定値としてコードから変更しない値を保持する際は、constまたはreadonlyのキーワードを使います。設定値の場合は変数の内容を変えれちゃうと困りますもんね。
どちらも後から値を変更できない点は共通していますが、コンパイル時に定数として扱われるか、実際に使うときに定数として扱われるかの違いがあります。
constはコンパイル時に定数として扱われます。コンパイルした結果、値を直書きした時と同じように扱われます。値を直書きするということは変数ではないので後から変更できないというのもわかりやすいですね。
例えば上記のように、『TodoStaticClass』というstaticなクラスを作成し、constなフィールドを作成します。このフィールドを別のクラスから使うときにはTodoStaticClass.nameという形でコードの中では書いているのですが、コンパイルしたときに中身が直書きした時と同じようになります。上のコードの8行目と9行目はコンパイルすると同じになる感じです。
IDEにもよりますが、VSCodeだと以下のようにカーソルをホバーさせると(constant)と表示され、定数であることが分かりやすくなっています。
もう一方のreadonlyは実行時に定数として扱われるものです。その名の通り読み取り専用になるので実質的に定数と同じように使えるよね、みたいな位置付けです。
コンパイル時にはまだ変数という扱いなので、constのように中身の値が直接書かれたりはしません。IDEでも中身が見えたりはしません。
同じように定数として扱う2通りの方法について、どう使い分けたらいいかを考えてみます。
const(コンパイル時定数)について
constはコンパイル時定数で、後から値を変更できません。constなフィールドを作った場合は、そのフィールドに値を入れようとすると怒られます。
コンパイル時に直書きと同じようにしてしまうので、ライブラリとして他の人に使ってもらう場合はあまり使わない方がいいかも? 同じゲーム内で使う分には大丈夫だと思います。というのも、ゲームをアップデートする際はまたUnityでビルド(コンパイル)しますからね。
上記のmsdnのドキュメントによれば、constは変わらない値に対して使うのがベスト。例えば円周率とか光の速さなどは不変なのでconstとして定義して使いましょ。
逆にゲームのバージョンなど、今後変わっていく可能性のあるものはconstを使わない方が良さそうです。値を変更する可能性がある場合はreadonlyにしておいた方がおすすめです。
constを使うケースだと……switch文の分岐として使いたい場合はconstの方が良いです。readonlyでは分岐のラベルとして使うことができないためです。
ただswitch文のラベルとして使うなら、Enumを定義した方が良いケースもあります。
複数の関連する値を管理する場合は、Enumで要素を決めてしまった方が安全です。constの場合は利用者側で値の範囲を確認しないといけないので、存在しない値が入ってくることもあります。Enumならコンパイラで値の範囲を確認してくれるので扱いやすいんですよね。
こう考えると、ゲーム内の設定値を保持する目的でconstを使うケースはあまりないかもしれませんね。public constとして使うよりも、ローカル変数としてそのメソッド内で共通して使うことが多いかも。
Unity特有の使用場面だと、Attributeの引数として使う場合もconstである必要があります。
readonly(実行時定数)について
readonlyは実行時定数で、こちらも後から値を変更できません。ただ、フィールドとして宣言する時と、コンストラクタを使ったときに値を割り当てることができます。コード内から実際に使うときには値をセットできないので定数のように扱うことができます。
ゲーム全体で使用する設定値はpublic static readonlyを使っておくと安全かもしれません。設定だと変更する可能性もありますからね。
外部にライブラリを公開しないとしても、readonlyをつけておくようにするのは大切かもしれません。
設定値を保持する目的ではあまりやらないかもしれませんが、参照型のオブジェクトに対する参照もreadonlyにすることができます。この場合は参照そのものを変更しないという点で読み取り専用なのですが、参照先のオブジェクトに関しては値を変えられたりします。readonlyの効果範囲も確認しておくとグッド。
実行するまで値が確定しないので、switch文のラベルなどには使用できません。
設定値を保持するならstatic readonlyがおすすめ
個人的にはstaticなクラスに設定値用のフィールドを作る場合、static readonlyを使うことが多いです。コンパイル時に値が確定していないといけないケースはconstを使用するようにしています。
readonly優先、どうしてもconstを使わなければならない理由があるならconstを使う、という方針になるかなと。
今回はゲーム全体で使いたい定義値や設定値を保持する目的なのでこの方針でしたが、どのような目的で使うのかを考えておくのは大事です。いやまぁこれを言い出したらキリがないんですけどね(笑)
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity/C#】必要に応じてアタッチしないクラスも作ると吉 2021.01.09
-
次の記事
サガフロンティアのリマスター版が出るそうで懐かしさを感じたので思い出を 2021.01.11
コメントを書く