C#: 静的メンバーと拡張メソッド
1. 静的フィールドとメソッド
staticのメンバーは、特定のインスタンスではなく、型そのものに属しています。すべてのインスタンスは同じ静的フィールドを共有します。静的メソッドは、オブジェクトを作成することなく、ClassName.Memberを介して呼び出されます。
▶ サンプル
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);
2 instances created
2
static メソッドは this を使用できず、インスタンスのメンバに直接アクセスすることもできません。他の静的メンバにのみアクセスできます。
2. 静的コンストラクタ
静的コンストラクタは、クラスが最初にアクセスされた際に一度だけ自動的に実行されます。アクセス修飾子やパラメータを指定することはできず、一般的に静的フィールドの初期化に使用されます。
▶ サンプル
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);
Preparing to access Config
Static constructor executed
2026/06/28 10:00:00
2026/06/28 10:00:00
3. 静的クラス(ユーティリティクラス・パターン)
static classで宣言されたクラスは、インスタンス化も継承もできません。すべてのメンバはstaticでなければならないため、ユーティリティメソッドをカプセル化するのに適しています。
▶ サンプル
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));
9
5
new MathHelper()を実行しようとすると、コンパイルエラーが発生します。静的クラスはインスタンス化できません。
4. const と static readonly の違い
| 特集 | const |
static readonly |
|---|---|---|
| 割り当て時 | コンパイル時 | 実行時 |
| 値型 | 組み込み型のみ | 任意の型 |
| 可変 | 不変 | コンストラクタでの代入後は不変 |
▶ サンプル
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);
3
2026/06/28 10:00:00
constの値は、コンパイル時に呼び出し元のアセンブリに埋め込まれます。constが変更された場合、それを参照しているすべてのアセンブリを再コンパイルする必要があります。static readonlyは実行時に読み込まれるため、より安全です。
5. 拡張メソッド
拡張メソッドは、既存の型を変更することなく、その型に「メソッドを追加」します。これらは静的クラス内で定義され、最初のパラメータにはthisというキーワードが接頭辞として付くため、インスタンスメソッドであるかのように呼び出すことができます。
▶ サンプル
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));
False
ababab
s.IsEmpty() を StringExt.IsEmpty(s) に変換します。インスタンスメソッドは拡張メソッドよりも優先されます。
6. 拡張メソッドの実践(LINQの基礎)
LINQのフルーエント構文は、拡張メソッドに基づいて構築されています。Where、Select、OrderBy、およびSystem.Linq.Enumerable内のその他のメソッドは、すべてIEnumerable<T>に対する拡張メソッドです。
▶ サンプル
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));
2, 4, 6
7. 部分クラス
キーワードpartialを使用すると、クラス定義を複数のファイルに分割することができ、コンパイル時にそれらが1つの完全なクラスに統合されます。これは、自動生成されたコードと手書きのコードを分離するために一般的に使用されます。
▶ サンプル
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();
Hello, Alice
8. partialメソッド
パーシャルメソッドは、パーシャルクラスのある部分で宣言され、必要に応じて別の部分で実装されます。実装されていない場合、コンパイラはその呼び出し箇所を削除するため、オーバーヘッドはゼロになります。C# 9以降、パーシャルメソッドにはシグネチャの制限がなくなりました。
▶ サンプル
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);
Order created
100
OnCreatedの実装を削除しても、プログラムはコンパイルされ、呼び出し箇所はコンパイラによって自動的に削除されます。
❓ よくある質問
📖 まとめ
staticのフィールドはすべてのインスタンスで共有されます。また、staticのメソッドはクラス名を通じて呼び出されます。- 静的コンストラクタは、クラスが最初にアクセスされた際に一度だけ実行され、静的メンバーの初期化に使用されます
static classはインスタンス化も継承もできません — ユーティリティクラスに最適ですconstはコンパイル時定数であり、static readonlyは実行時定数である- 拡張メソッドは、
thisパラメータを使用して既存の型にメソッドを追加します。これがLINQの基礎となっています。 partialクラスは定義を複数のファイルに分割し、コンパイル時に統合されるpartialメソッドはオプションのフックを提供します。実装されていない呼び出しはコンパイラによって削除されます。
📝 練習問題
static class ArrayHelperクラスに、静的メソッドMax(int[] arr)とAverage(int[] arr)を実装し、Mainクラスでそれらをテストする- LINQ を使用せずに整数の数列の和を計算する、
IEnumerable<int>に対する拡張メソッドMySumを作成してください。 partial class Loggerを作成する。一方では、partial void OnLog(string message)とLog(string msg)メソッド(内部でOnLogを呼び出す)を宣言し、もう一方では、コンソールに出力するためにOnLogを実装するconstフィールドとstatic readonlyフィールドを定義し、const がコンパイル時に置換されるのに対し、static readonly は実行時に読み込まれることを確認するコードを記述してください(ヒント:別のアセンブリからこれらを参照し、変更後の挙動を観察してください)。