プログラミングにおける「定数」の重要性
プログラミングにおいて、「定数」とは、一度値を設定したらプログラムの実行中に変更できない値のことを指します。C#では、このような値を定義するために主にconstキーワードが用いられます。
定数を利用することで、コードの可読性が向上します(例:3.14159という数値よりもPIという名前の方が意図が明確です)。また、将来的にその値を変更する必要が出た場合でも、定義箇所を一箇所修正するだけで済むため、保守性が大幅に向上します。
この記事では、constキーワードの基本的な使い方、ルール、そしてよく似たreadonlyキーワードとの明確な違いについて解説します。
const キーワードの基本的な使い方
constは「constant(定数)」の略で、変数を定数として宣言するために使用します。
constで宣言された値は、プログラムのコンパイル時にその値が確定し、アプリケーションの実行中は絶対に変わらない値として扱われます。
constの定義例
constは、メソッド内(ローカル定数)またはクラスのメンバーとして定義できます。
using System;
// アプリケーション設定クラス内での定義例
public class AppConfiguration
{
// データベースのデフォルト・タイムアウト時間(ミリ秒)
public const int DefaultTimeoutMilliseconds = 30000;
// アプリケーションの固定的な設定キー名
public const string ApiKeyName = "MyApp:ServiceApiKey";
}
public class Program
{
// 重力加速度(物理定数)
private const double GravityAcceleration = 9.80665;
public static void Main()
{
// メソッド内で定義するローカル定数
const string WelcomeMessage = "処理を開始します。";
Console.WriteLine(WelcomeMessage);
// クラス定数へのアクセス(暗黙的にstaticとして扱われる)
Console.WriteLine($"デフォルトのタイムアウト: {AppConfiguration.DefaultTimeoutMilliseconds} ms");
Console.WriteLine($"使用する重力加速度: {GravityAcceleration} m/s^2");
}
}
const の主要なルール
constを使用する際には、いくつかの厳格なルールに従う必要があります。
1. 宣言と同時に初期化が必須
constで宣言する値は、必ず宣言と同時に値を代入しなければなりません。後から値を代入することはできません。
// 有効な例
const int MaxRetryCount = 5;
// 無効な例(コンパイルエラー)
// const int MaxRetryCount;
// MaxRetryCount = 5;
2. コンパイル時に値が決定していること
constの値は、プログラムのコンパイル時に確定していなければなりません。そのため、実行時に評価される値(メソッドの戻り値、newによるオブジェクト生成など)を代入することはできません。
// 有効な例(コンパイル時に値が確定)
const int ValueA = 10;
const int ValueB = 20 * 2;
const string AppName = "MyApplication";
// 無効な例(実行時に値が決定されるためコンパイルエラー)
// const DateTime StartTime = DateTime.Now;
// const Guid InstanceId = Guid.NewGuid();
3. 暗黙的に static として扱われる
クラスのメンバーとしてconstを宣言した場合、それは自動的にstatic(静的)メンバーとして扱われます。インスタンスを作成しなくても、クラス名から直接アクセスできます。
constにstaticキーワードを明示的に付けることはできません(不要であり、コンパイルエラーとなります)。
const と readonly の明確な違い
C#にはconstとよく似たキーワードとしてreadonlyがあります。どちらも「変更できない値」を定義するために使われますが、その特性は大きく異なります。
const(コンパイル時定数)
- 値がコンパイル時に決定される必要があります。
- 宣言時に必ず値を初期化しなければなりません。
- 暗黙的に
staticです。 - 使用できる型は、数値型、
bool、char、string、enumなどの組み込み型に限られます。
readonly(実行時定数)
- 値が実行時に決定されても構いません。
- 宣言時、またはクラスのコンストラクター内で一度だけ値を初期化できます。
static(クラス定数)にも、インスタンスメンバー(インスタンスごとに固有の定数)にもできます。constで使えない型(DateTime,Guidなど)も使用できます。
比較表
| 特性 | const | readonly |
| 値の決定時期 | コンパイル時 | 実行時(コンストラクター実行時) |
| 初期化の場所 | 宣言時のみ | 宣言時 または コンストラクター内 |
static指定 | 暗黙的にstatic(指定不可) | 明示的にstatic指定が可能(インスタンスメンバーも可) |
| 使用可能な型 | 基本型, string, enum | ほぼ全ての型(参照型も含む) |
readonly の使用例
constでは定義できない、実行時に決定される固定値を定義する場合にreadonlyは非常に有効です。
using System;
public class LogProcessor
{
// readonly: インスタンスごとに固定される値
// このインスタンスが生成された時刻を保持する
private readonly DateTime _creationTimestamp;
// static readonly: クラスで共有される実行時定数
// プロセスが開始されたIDを保持する
private static readonly Guid ProcessId = Guid.NewGuid();
// コンストラクターで readonly フィールドを初期化
public LogProcessor()
{
this._creationTimestamp = DateTime.UtcNow;
}
public void WriteLog(string message)
{
// readonly の値を利用する
Console.WriteLine($"[{ProcessId}] [{this._creationTimestamp:O}] {message}");
}
}
public class Program
{
public static void Main()
{
LogProcessor logger1 = new LogProcessor();
System.Threading.Thread.Sleep(100); // 100ミリ秒待機
LogProcessor logger2 = new LogProcessor();
// 2つのインスタンスは、異なる_creationTimestamp(インスタンスごとのreadonly)を持つが、
// 同じProcessId(static readonly)を共有する。
logger1.WriteLog("Logger 1 started.");
logger2.WriteLog("Logger 2 started.");
}
}
まとめ
constキーワードは、プログラムのどこからも変更されず、かつコンパイル時に値が確定している普遍的な値(数学定数、固定的な設定キー名など)を定義する際に使用します。
一方、インスタンスの生成時やプログラムの起動時など、実行時に一度だけ決定される値を固定したい場合はreadonlyキーワードを使用します。
これらの違いを理解し、値の特性に応じて適切に使い分けることが、堅牢で保守性の高いC#コードを作成するために重要です。
