静的メンバ
静的メンバとは
前回まで学習してきたフィールドやメソッドは、インスタンスを生成してから利用するものでした。しかし、C#にはインスタンスを生成しなくても利用できるフィールドやメソッドが存在します。このようなメンバのことを静的メンバ(スタティックメンバ)と言います。
静的メンバは、フィールドやメソッドの宣言に static 修飾子を付けることで定義できます。インスタンスメンバと違い、クラス全体で共有される1つのデータや処理として扱われます。
サンプルプログラム
以下のサンプルを入力・実行してみてください。生成された Data オブジェクトの数を静的フィールドで管理しています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx201
{
class Data
{
private static int num = 0; // 静的フィールド(オブジェクト数)
private int id;
public Data(int id)
{
this.id = id;
num++;
Console.WriteLine("値:{0} 数:{1}", this.id, num);
}
public static void ShowNumber()
{
Console.WriteLine("Dataオブジェクトの数:{0}", num);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx201
{
class Program
{
static void Main(string[] args)
{
Data[] d = new Data[2];
Data.ShowNumber(); // インスタンスなしで呼び出し
for (int i = 0; i < d.Length; i++)
{
d[i] = new Data(i * 100);
Data.ShowNumber();
}
}
}
}
値:0 数:1
Dataオブジェクトの数:1
値:100 数:2
Dataオブジェクトの数:2
静的メンバの呼び出し方
静的メンバは、インスタンスを生成せずに (クラス名).(メンバ名) という形式で呼び出します。このサンプルでは、Data.ShowNumber() のようにクラス名を使って静的メソッドを呼び出しています。
静的フィールドの特性
静的フィールド num は、クラスに対して1つだけ存在します。つまり、どのインスタンスからアクセスしても同じ変数を参照することになります。このサンプルでは、Data オブジェクトが生成されるたびにコンストラクタ内で num++ を実行することにより、生成済みのオブジェクト数をカウントしています。
num と記述できます。インスタンスメソッドから静的メンバへのアクセスは可能ですが、静的メソッドからインスタンスメンバへのアクセスはできません。プログラムの流れ
SampleEx201 のプログラムがどのように実行されるかを、図で確認してみましょう。静的フィールド num はクラスに対して1つだけ存在し、インスタンスの生成に伴って値が変化していきます。
図2-1: プログラム開始直後、Data クラスのインスタンスはまだ1つも生成されていません。静的フィールド num はクラスが読み込まれた時点で 0 に初期化されており、Data.ShowNumber() を呼ぶと「0」と表示されます。
図2-1. Dataクラスのインスタンスが生成される前の処理
図2-2: 最初のインスタンス(d[0])が生成されると、コンストラクタが呼ばれて num++ が実行され、num が 1 に増加します。静的フィールド num はクラスで唯一の変数なので、どのインスタンスからアクセスしても同じ値を参照できます。
図2-2. Dataクラスのインスタンスが一つ生成されたときの状態
図2-3: 2つ目のインスタンス(d[1])が生成されると、再びコンストラクタが呼ばれ num が 2 に増加します。インスタンスフィールド id は各インスタンスごとに独立していますが、静的フィールド num はクラスに1つだけ存在し続けます。
図2-3. Dataクラスのインスタンスの二つ目が生成されたときの状態
Main メソッド
Main メソッドは静的メソッド
ここで一度、これまで使ってきた Main メソッドについて振り返ってみましょう。Main メソッドは、プログラムの実行時に最初に呼び出される特殊なメソッドです。
Main メソッドの宣言を見ると、static void Main(string[] args) となっており、static が付いていることが分かります。つまり、Main メソッドは静的メソッドの一種なのです。プログラム起動時点ではインスタンスが存在しないため、エントリーポイントである Main メソッドは静的メソッドでなければなりません。なお、1つのプロジェクトに Main メソッドは1つしか置けません。
サンプルプログラム
以下のサンプルでは、Program クラスが自身のインスタンスを生成し、インスタンスメンバと静的メンバの両方にアクセスしています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx202
{
class Program
{
private static int snum = 100; // 静的フィールド
public int inum = 200; // インスタンスフィールド
public static void Foo()
{
Console.WriteLine("Fooメソッド(staticメソッド)");
}
public void Bar()
{
Console.WriteLine("Barメソッド(インスタンスメソッド)");
}
static void Main(string[] args)
{
Program p = new Program();
Console.WriteLine("pのインスタンスフィールド: inum = {0}", p.inum);
Console.WriteLine("Programのクラスフィールド: snum = {0}", snum);
Foo();
p.Bar();
}
}
}
Programのクラスフィールド: snum = 100
Fooメソッド(staticメソッド)
Barメソッド(インスタンスメソッド)
静的メンバとインスタンスメンバのアクセス規則
Main メソッドは静的メソッドなので、同じクラスの静的フィールド snum や静的メソッド Foo() にはそのままアクセスできます。一方、インスタンスメンバである inum や Bar() にアクセスするには、インスタンス p を生成してから p.inum、p.Bar() のようにアクセスする必要があります。
| アクセス元 | 静的メンバへのアクセス | インスタンスメンバへのアクセス |
|---|---|---|
| 静的メソッド | ○ 可能 | × 不可(インスタンス経由なら可) |
| インスタンスメソッド | ○ 可能 | ○ 可能 |