static は、オブジェクトではなく型に属するメンバーを表します。インスタンスフィールドはオブジェクトごとに1つ存在し、静的フィールドは全体でただ1つ存在します。インスタンスメソッドはオブジェクトに対して実行され(order.Ship())、静的メソッドは型に対して実行され(Math.Round(x))、オブジェクトをまったく必要としません。
静的フィールド:1つのコピーを共有する
違いが最もよくわかるのはカウンターです。各 Order は独自の Id を持ちますが、これまでに作られた注文の数は、共有された場所に置く必要があります。
出力:
Ana #1, Ben #2, Chen #3
Orders created: 3
静的メンバーには、a.Created ではなく Order.Created のように型名を通してアクセスします。C#ではインスタンスを通して静的メンバーにアクセスできない(エラーCS0176)ので、呼び出し箇所で値が共有されていることが明らかになります。
静的メソッド
静的メソッドには this がありません。扱えるのは引数と他の静的メンバーだけです。そのため、入力から何かを計算するヘルパーに自然な形です。
出力:
61.38 EUR
7
Main 自体も静的です。ランタイムは、どんなオブジェクトも存在しないうちにそれを呼び出します。
CS0120のエラー:インスタンスメンバーを使う静的なコード
Main が静的なので、初心者は2つ目のメソッドを追加した途端にこのエラーによく出会います。
class Program
{
int visits;
void Greet() { Console.WriteLine("Hello"); }
static void Main()
{
Greet(); // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
visits++; // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
}
}
静的メソッドにはオブジェクトがないので、インクリメントすべき visits もありません。直し方は2つあり、正しいのはメンバーが何であるかによります。
- メンバーが本当にオブジェクトごとのデータなら:オブジェクトを作って使います。
var app = new Program(); app.Greet(); - メンバーがオブジェクトの状態を必要としないなら:
staticを付けます。static void Greet() { ... }
エラーを消すためにすべてに static を付けるのは、小さなプログラムなら動きますが、実際のプログラムではグローバルな状態になってしまいます。データが1つのオブジェクトに属するのか、プログラム全体に属するのかを考えて決めます。
逆方向は常に許されています。インスタンスメソッドは静的メンバーを自由に呼び出せます。
静的クラス
static として宣言されたクラスは静的メンバーしか持てず、決してインスタンス化できません。関数と定数に名前を付けたグループです。
出力:
77
100
True
コンパイラはこの約束を強制します。静的クラスの中のインスタンスメンバーはエラーCS0708で、それに対する new はエラーCS0712です。Math、Console、File、Path、Enumerable はすべて静的クラスです。拡張メソッドも静的クラスの中に置かなければなりません。
静的コンストラクターとstatic readonly
値に1つより多くの式が必要な静的フィールドは、静的コンストラクターで設定できます。ランタイムは型が最初に使われる前に、それを1回実行します。
出力:
Before first use
ShippingRates initialized
5.50
3
2026-01-01
readonly は、フィールドに代入できるのが初期化子か静的コンストラクターの中だけであることを意味します。その後は再代入できませんが、指しているオブジェクトはまだ変更可能かもしれません。だから辞書を IReadOnlyDictionary として公開しています。
constとstatic readonly
どちらもプログラム全体で共有される名前付きの値を与え、違いは値がいつ固定されるかです。
const | static readonly | |
|---|---|---|
| 値が固定されるとき | コンパイル時 | 実行時に1回 |
| 使える型 | 数値、char、bool、string、enum、null | 任意の型 |
| 暗黙的に静的か | はい(static const とは書けない) | 明示的にstaticと宣言する |
| 呼び出し側のアセンブリにコピーされるか | はい | いいえ、フィールドから読む |
switch のcaseや属性の引数に使えるか | はい | いいえ |
コピーの行はライブラリにとって重要です。アセンブリAが public const int MaxItems = 50; を宣言し、アセンブリBがそれを使うと、Bにはリテラルの 50 が含まれます。Aを 100 に変えても、Bを再コンパイルするまでBには何の影響もありません。バージョン間で変わるかもしれない値には static readonly を優先します。
using static
using static ディレクティブ(C# 6)は型の静的メンバーをインポートするので、型名なしで呼び出せます。
出力:
Area: 28.27
40
数学的な処理の多いコードでは読みやすくなります。それ以外では控えめに使います。単に WriteLine や Parse と書くと、メソッドがどの型から来たのかが隠れてしまいます。
静的な状態とスレッド
静的フィールドは、プロセス内のすべてのスレッドで共有されます。複数のスレッドから同時にインクリメントすると更新が失われます。created++ は読み取り、加算、書き込みの3つで、2つのスレッドがそれらを交互に実行することがあるからです。カウンターには Interlocked.Increment(ref created) を、それより大きなものにはlockを使います。これが問題になりやすいのはWebアプリケーションです。すべてのリクエストはどこかのスレッドで実行されるので、「現在のユーザー」を保持する静的フィールドは全ユーザーで共有されてしまいます。
もう1つ微妙な点があります。ジェネリッククラスでは、構築された型ごとに独自の静的フィールドを持ちます。Cache<int>.Count と Cache<string>.Count は別々のフィールドです。
staticを使うべきとき
良い使い方:
- 引数から結果を計算する純粋なヘルパー:書式設定、変換、検証。
- プログラムで共有される定数と読み取り専用の参照データ。
TimeSpan.FromMinutes(5)や自分で作るTemperature.FromCelsius(21)のようなファクトリーメソッド。- 拡張メソッド。
良くない使い方:
- 変更可能なグローバルな状態(現在のユーザー、ショッピングカート、変わる設定)。それに触れるすべてのコードを結び付け、テストが互いの残りものに依存するようになります。
- 時計、データベース、メール送信など、テストで差し替えたくなるものすべて。静的な呼び出しは偽物に置き換えられませんが、コンストラクターに渡したインスタンスなら置き換えられます。
よくある質問
C#のstaticとはどういう意味ですか?
static のメンバーは、どのオブジェクトでもなく型そのものに属します。オブジェクトがいくつあっても静的フィールドのコピーは1つだけで、静的メソッドはインスタンスを作らずに型名に対して呼び出します(Math.Max(3, 7))。静的メソッドには this がないので、インスタンスフィールドを直接使えません。
C#の静的クラスとは何ですか?
static として宣言されたクラスは静的メンバーしか持てず、決してインスタンス化できません(それに new を使うとエラーCS0712)。Math、Console、File のように、オブジェクトの状態を必要としない関連する関数をまとめます。拡張メソッドは静的クラスの中で宣言しなければなりません。
「An object reference is required for the non-static field, method, or property」はどう直しますか?
これはエラーCS0120で、静的メソッド(多くの場合 Main)がインスタンスメンバーを使っています。オブジェクトを作ってそのメンバーを呼ぶ(var app = new Program(); app.Run();)か、オブジェクトごとのデータが不要ならメンバーに static を付けます。
C#のconstとstatic readonlyの違いは何ですか?
const はコンパイル時定数(数値、文字列、bool、enum)で、その値はそれを使うすべてのアセンブリにコピーされます。static readonly のフィールドは、初期化子か静的コンストラクターで実行時に1回だけ代入され、DateTime やリストなど任意の型を保持できます。決して変わらない値には const を、それ以外には static readonly を使います。
C#ではいつstaticを使うべきですか?
引数だけを扱うコード(価格の書式設定のような純粋なヘルパー)、本当にプログラム全体で共通の定数やキャッシュ、ファクトリーメソッドにはstaticを使います。ユーザーごとやリクエストごとの状態を保持するもの、テストで置き換えたくなるものにはstaticを避けます。共有された変更可能な状態はテストが難しく、ロックなしではスレッド間で安全ではありません。