プログラムで「この値は絶対に変わらない」と宣言したいとき、C#ではconstとreadonlyという2つのキーワードが候補に挙がります。どちらも変更不可ですが、動作や使いどころに決定的な違いがあります。本記事ではconstとreadonlyの違いを、最新情報にもとづいてパフォーマンス・型・初期化・アセンブリ互換性など多角的に比較し、実践的な使い分けと注意点まで詳しく解説します。
C# 定数 readonly const 違い を理解するための基本概念
まずconstとreadonlyそれぞれの基本的な定義と性質を知ることから始めます。C# 定数 readonly const 違いを理解する上で、どこで何が決まるのか、どの型が使えるのかを明確に理解しておくことが肝心です。
constとは何か
constはコンパイル時にその値が確定する定数を宣言するキーワードです。宣言と同時に値を代入する必要があり、その後は変更できません。参照型では文字列とヌル以外は const にできず、ユーザー定義型や配列、オブジェクトなどは使えません。
readonlyとは何か
readonlyは実行時に一度だけ初期化でき、その後変更できないフィールドを示します。宣言時に値を代入するか、クラスまたは構造体のコンストラクタで設定可能です。静的な static readonly フィールドもあり、実行時初期化や外部からの参照に柔軟性があります。
型と適用範囲の違い
constは主にビルトインの数値、文字列、列挙型などプリミティブ型に限定され、複雑な型には適用できません。一方、readonlyは任意の型のフィールドに対して使えるため、クラス型や構造体型のオブジェクトの参照を不変に保つ宣言としても使えます。ただし参照型の内部状態が変更できることには注意が必要です。
constとreadonlyの比較:機能・性能・使いどころ
次に、C# 定数 readonly const 違い の中核とも言える機能的な比較を行います。初期化タイミング、アクセス方法、性能への影響、アセンブリの互換性など具体的な観点から違いを整理します。これによりどちらを選ぶべきか判断がしやすくなります。
初期化タイミングの違い:コンパイル時 vs 実行時
constはコンパイル時に値が確定し、IL(中間言語)コードにリテラル値として埋め込まれます。値はコード内で直接置き換えられるため、実行時の初期化動作は発生しません。
readonlyは宣言時かコンストラクタ/静的コンストラクタの中で初期化され、実行開始時あるいはオブジェクト生成時に値が決まります。複数のコンストラクタで異なる値を設定することも可能です。
アクセスと参照:static性とアセンブリ間の振る舞い
const は暗黙的に static とみなされ、クラスインスタンスを通さずアクセスします。他アセンブリから参照されるとき、const は使用先にリテラルとして埋め込まれており、元の定数値が変わっても再コンパイルされるまで参照先の値は古いままになります。
static readonly は static フィールドであり、参照は実行時にフィールドから読み込まれます。他アセンブリで値を変更した場合、ライブラリを更新すれば新しい値を取得できます。アセンブリ間互換性を重視する場面ではこちらが望ましい選択肢です。
性能上の差異とメモリの扱い
const はリテラルが埋め込まれるため、実行時のアクセスコストがほぼゼロです。メモリアクセスや参照なしで直接値が参照されるため、小さな値や頻繁に使われる定数には有利です。
readonly は値取得時にフィールドにアクセスするため、多少の間接参照コストが発生しますが、通常は無視できるレベルです。実行時初期化やオブジェクト毎の違いが必要な場面ではこちらを選ぶ価値があります。
アセンブリおよびバージョン管理での注意点
const を public にして他アセンブリが参照している場合、ライブラリ内で const の値を変更しても、参照先のアセンブリが再コンパイルされない限り古い値が使われ続けます。これが予期しないバグの原因になることがあります。
readonly はフィールド参照で動作するため、ライブラリ更新時に値を変えて再デプロイすれば、新しい値がすぐ反映されます。バージョン互換性を維持するには、public な定数はreadonlyか static readonly を検討すべきです。
実践的な使い分け:どちらをいつ使うか
C# 定数 readonly const 違い を理解した上で、実際の開発でどのように使い分けるかが重要です。この章では、代表的なユースケース・アンチパターン・命名規約・可読性の観点から判断基準を示します。
const を使うべきケース
以下のようなケースで const の使用が適しています:
- 値が普遍的であり将来にわたって変わる見込みがないもの(円周率、真偽値、数値定数など)。
- 文字列リテラルなどビルトイン型のリテラルであるもの。
- アトリビュートの引数など、コンパイル時定数が要求される場所。
readonly(または static readonly)を使うべきケース
以下のような場面では readonly が適切です:
- 値が実行時に決定される必要があるもの(設定ファイルからロード、環境変数、GUID、日時など)。
- 各インスタンスごとに異なる値を持たせたい場合。
- 将来の変更やバージョンアップによって値を更新したいライブラリの定義。
アンチパターンとよくある誤用
const を使う場合の◯落とし穴として、外部アセンブリをまたぐ変更が反映されないことがあります。また、const で非プリミティブ型を無理に使おうとしてコンパイルエラーになることも頻発します。
readonly の誤用は、参照型の内部データを mutable にしてしまうことです。readonly は参照そのものを固定しますが、オブジェクトの内部状態を防げるものではありません。この点を混同しないことが可読性と安全性の鍵です。
比較表で一目瞭然:const vs readonly
| 特徴 | const | readonly / static readonly |
|---|---|---|
| 初期化タイミング | 宣言時のみ | 宣言時またはコンストラクタ/静的コンストラクタ |
| 型の制限 | ビルトイン型のみ、文字列と真偽値など | 任意の型可(参照型・構造体など) |
| static性 | 暗黙の static | 必要なら指定可能、インスタンスフィールドも可 |
| アセンブリの互換性 | 変更反映に再コンパイル必須 | ライブラリ更新で変更が即時反映可能 |
| 性能 | コンパイル時埋め込みで高速 | わずかなアクセスコストありだが実用的には問題なし |
C# 定数 readonly const 違い に関する最新仕様と補足事項
最新情報です。C#言語仕様や.NETの実装で追加されたルール・細かい挙動を補足しておきます。これを知っておくと限界・例外が見えて守りやすくなります。
参照型の readonly フィールドが持つ制約
参照型のフィールドを readonly として宣言すると、参照そのもの(オブジェクトのポインタ)が変更できなくなりますが、オブジェクト内部の状態を変更することは可能です。オブジェクトが不変でない限り、データの整合性に注意を払うべきです。
readonly struct とメソッドへの適用
C#では構造体に readonly 修飾子を付けて不変性を保証することができ、readonly なメソッドやプロパティも状態を変更しないよう制限されます。これによりパフォーマンスやスレッド安全性が向上するケースがあります。
アトリビュート引数と const の要件
アトリビュートの引数として渡せる値はコンパイル時定数である必要があります。つまり const のみが使える場面です。readonly や static readonly はアトリビュート引数には使えません。
enumとの関係性
列挙型(enum)は複数の名前付き定数を持てる型であり、enum のメンバーは整数型の定数として扱われます。enum は const のように利用でき、列挙値として利用できる場面では const よりむしろenumが適切です。
実例で学ぶコードサンプルと応用場面
理論だけでなく、実際にコードでどう使うかを見ておくと理解が深まります。C# 定数 readonly const 違い を踏まえて実践的な例を複数示します。
数値・文字列のユーティリティ定数
たとえば PI やメッセージ文字列など絶対に変わらない値に対しては const を使用します。例として public const double PI = 3.141592653589793; や public const string ErrorMsg = “不正な値です”; のような使い方が典型的です。
設定値を読み込んで readonly で保持するケース
アプリケーション設定ファイルから読み込むタイムアウト値や ID 発行時の日時、環境変数に応じた設定などは、実行時に初期化する必要があります。そのような値を static readonly や readonly インスタンスフィールドで保持します。コンストラクタや静的コンストラクタ内で安全に設定できます。
ライブラリ開発時の公開 API での意図的設計
ライブラリの公開型で public 定数として値を外部に提供する場合、将来的な値変更を見越して readonly を使う方が安全です。const を使うと参照先アセンブリが古い値を持ったままになることがあります。使用者側への影響を考えて選択すべきです。
まとめ
C# 定数 readonly const 違い を整理すると、
- const は「コンパイル時定数」。ビルトイン型のみ対応し、宣言時に値を代入する必要がある。
- readonly は「実行時初期化可」。任意型に使え、宣言時またはコンストラクタで値を設定できる。
- 性能やアセンブリ互換性などの観点からケースバイケースで使い分けることが重要。
基本的な指針としては、値が本当に変わらないものは const、実行時に変わるかもしれないものは readonly や static readonly を選び、将来の保守性と可読性を重視する設計を心がけていただきたいです。
コメント