C#: 静的メンバーと拡張メソッド

1. 静的フィールドとメソッド

staticのメンバーは、特定のインスタンスではなく、型そのものに属しています。すべてのインスタンスは同じ静的フィールドを共有します。静的メソッドは、オブジェクトを作成することなく、ClassName.Memberを介して呼び出されます。

▶ サンプル

CSHARP
class Counter
{
    public static int TotalCount = 0;
    public int InstanceId;

    public Counter(int id)
    {
        InstanceId = id;
        TotalCount++;
    }

    public static void Report()
    {
        Console.WriteLine($"{TotalCount} instances created");
    }
}

Counter a = new Counter(1);
Counter b = new Counter(2);
Counter.Report();
Console.WriteLine(Counter.TotalCount);
▶ 試してみよう
TEXT 📖 参照専用
2 instances created
2
💡 static メソッドは this を使用できず、インスタンスのメンバに直接アクセスすることもできません。他の静的メンバにのみアクセスできます。


2. 静的コンストラクタ

静的コンストラクタは、クラスが最初にアクセスされた際に一度だけ自動的に実行されます。アクセス修飾子やパラメータを指定することはできず、一般的に静的フィールドの初期化に使用されます。

▶ サンプル

CSHARP
class Config
{
    public static readonly DateTime StartTime;

    static Config()
    {
        StartTime = DateTime.Now;
        Console.WriteLine("Static constructor executed");
    }
}

Console.WriteLine("Preparing to access Config");
Console.WriteLine(Config.StartTime);
Console.WriteLine(Config.StartTime);
▶ 試してみよう
TEXT 📖 参照専用
Preparing to access Config
Static constructor executed
2026/06/28 10:00:00
2026/06/28 10:00:00
⚠️ 静的コンストラクタは一度だけ呼び出されます。クラスの静的メンバーに複数回アクセスしても、再度実行されることはありません。


3. 静的クラス(ユーティリティクラス・パターン)

static classで宣言されたクラスは、インスタンス化も継承もできません。すべてのメンバはstaticでなければならないため、ユーティリティメソッドをカプセル化するのに適しています。

▶ サンプル

CSHARP
static class MathHelper
{
    public static double Square(double x) => x * x;
    public static double Hypotenuse(double a, double b) => Math.Sqrt(Square(a) + Square(b));
}

Console.WriteLine(MathHelper.Square(3));
Console.WriteLine(MathHelper.Hypotenuse(3, 4));
▶ 試してみよう
TEXT 📖 参照専用
9
5
📌 new MathHelper()を実行しようとすると、コンパイルエラーが発生します。静的クラスはインスタンス化できません。


4. const と static readonly の違い

特集 const static readonly
割り当て時 コンパイル時 実行時
値型 組み込み型のみ 任意の型
可変 不変 コンストラクタでの代入後は不変

▶ サンプル

CSHARP
class Settings
{
    public const int MaxRetry = 3;
    public static readonly DateTime LaunchTime;

    static Settings()
    {
        LaunchTime = DateTime.Now;
    }
}

Console.WriteLine(Settings.MaxRetry);
Console.WriteLine(Settings.LaunchTime);
▶ 試してみよう
TEXT 📖 参照専用
3
2026/06/28 10:00:00
⚠️ constの値は、コンパイル時に呼び出し元のアセンブリに埋め込まれます。constが変更された場合、それを参照しているすべてのアセンブリを再コンパイルする必要があります。static readonlyは実行時に読み込まれるため、より安全です。


5. 拡張メソッド

拡張メソッドは、既存の型を変更することなく、その型に「メソッドを追加」します。これらは静的クラス内で定義され、最初のパラメータにはthisというキーワードが接頭辞として付くため、インスタンスメソッドであるかのように呼び出すことができます。

▶ サンプル

CSHARP
static class StringExt
{
    public static bool IsEmpty(this string s) => s.Length == 0;
    public static string Repeat(this string s, int count)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < count; i++) sb.Append(s);
        return sb.ToString();
    }
}

Console.WriteLine("hello".IsEmpty());
Console.WriteLine("ab".Repeat(3));
▶ 試してみよう
TEXT 📖 参照専用
False
ababab
💡 拡張メソッドは本質的に静的メソッドの呼び出しであり、コンパイラは s.IsEmpty()StringExt.IsEmpty(s) に変換します。インスタンスメソッドは拡張メソッドよりも優先されます。


6. 拡張メソッドの実践(LINQの基礎)

LINQのフルーエント構文は、拡張メソッドに基づいて構築されています。WhereSelectOrderBy、およびSystem.Linq.Enumerable内のその他のメソッドは、すべてIEnumerable<T>に対する拡張メソッドです。

▶ サンプル

CSHARP
static class EnumerableExt
{
    public static IEnumerable```<T>``` MyWhere```<T>```(this IEnumerable```<T>``` source, Func`<T, bool>` predicate)
    {
        foreach (var item in source)
        {
            if (predicate(item)) yield return item;
        }
    }
}

int[] nums = { 1, 2, 3, 4, 5, 6 };
var even = nums.MyWhere(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", even));
▶ 試してみよう
TEXT 📖 参照専用
2, 4, 6
🚀 拡張メソッドの仕組みを理解すれば、LINQのソースコードを読むのも簡単になります。LINQは、単にうまく設計された拡張メソッドの集まりに過ぎないからです。


7. 部分クラス

キーワードpartialを使用すると、クラス定義を複数のファイルに分割することができ、コンパイル時にそれらが1つの完全なクラスに統合されます。これは、自動生成されたコードと手書きのコードを分離するために一般的に使用されます。

▶ サンプル

CSHARP
partial class User
{
    public string Name { get; set; }
    public User(string name) => Name = name;
}

partial class User
{
    public void Greet() => Console.WriteLine($"Hello, {Name}");
}

var u = new User("Alice");
u.Greet();
▶ 試してみよう
TEXT 📖 参照専用
Hello, Alice
📌 WinForms、WPF、EF Core などのフレームワークでは、デザイナーによって生成されたコードとビジネスロジックを分離するために、パーシャルクラスが広く活用されています。


8. partialメソッド

パーシャルメソッドは、パーシャルクラスのある部分で宣言され、必要に応じて別の部分で実装されます。実装されていない場合、コンパイラはその呼び出し箇所を削除するため、オーバーヘッドはゼロになります。C# 9以降、パーシャルメソッドにはシグネチャの制限がなくなりました。

▶ サンプル

CSHARP
partial class Order
{
    public int Amount { get; set; }
    partial void OnCreated();

    public Order(int amount)
    {
        Amount = amount;
        OnCreated();
    }
}

partial class Order
{
    partial void OnCreated() => Console.WriteLine("Order created");
}

var o = new Order(100);
Console.WriteLine(o.Amount);
▶ 試してみよう
TEXT 📖 参照専用
Order created
100
💡 2番目の部分クラスからOnCreatedの実装を削除しても、プログラムはコンパイルされ、呼び出し箇所はコンパイラによって自動的に削除されます。

❓ よくある質問

Q 静的メソッドはインスタンスフィールドにアクセスできますか?
A いいえ。静的メソッドはどのインスタンスとも関連付けられていません。インスタンスのメンバーにアクセスするには、オブジェクト参照を使用する必要があります。
Q const と static readonly のどちらを使うべきですか?
A コンパイル時に既知の単純な定数には const を使い、実行時に決定される値や参照型には static readonly を使います。
Q 拡張メソッドはプライベートメンバーにアクセスできますか?
A いいえ。拡張メソッドは本質的に静的メソッドであり、パブリックメンバーにしかアクセスできません。
Q パーシャルクラスのすべての部分は、同じアセンブリ内に存在する必要がありますか?
A はい、パーシャルクラスのすべての部分は、同じアセンブリおよび同じ名前空間内に存在する必要があります。

📖 まとめ

📝 練習問題

  1. static class ArrayHelperクラスに、静的メソッドMax(int[] arr)Average(int[] arr)を実装し、Mainクラスでそれらをテストする
  2. LINQ を使用せずに整数の数列の和を計算する、IEnumerable<int> に対する拡張メソッド MySum を作成してください。
  3. partial class Loggerを作成する。一方では、partial void OnLog(string message)Log(string msg)メソッド(内部でOnLogを呼び出す)を宣言し、もう一方では、コンソールに出力するためにOnLogを実装する
  4. constフィールドとstatic readonlyフィールドを定義し、const がコンパイル時に置換されるのに対し、static readonly は実行時に読み込まれることを確認するコードを記述してください(ヒント:別のアセンブリからこれらを参照し、変更後の挙動を観察してください)。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%