Menu

C#のstatic:静的クラス、静的メソッド、静的フィールド、静的コンストラクター

C#のstaticの意味を解説します。オブジェクトではなく型に属するメンバー、インスタンス間で共有される状態、静的メソッドと静的クラス、静的コンストラクター、constとstatic readonlyの違い、using static、そしてCS0120のエラーを扱います。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

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

どちらもプログラム全体で共有される名前付きの値を与え、違いは値がいつ固定されるかです。

conststatic 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を避けます。共有された変更可能な状態はテストが難しく、ロックなしではスレッド間で安全ではありません。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める