応用編

応用編2日目:静的メンバ

インスタンスを生成せずに使える静的フィールドと静的メソッドを学びましょう

応用編: 1 2 3 4 5 6 7

静的メンバ

静的メンバとは

前回まで学習してきたフィールドやメソッドは、インスタンスを生成してから利用するものでした。しかし、C#にはインスタンスを生成しなくても利用できるフィールドやメソッドが存在します。このようなメンバのことを静的メンバ(スタティックメンバ)と言います。

静的メンバは、フィールドやメソッドの宣言に static 修飾子を付けることで定義できます。インスタンスメンバと違い、クラス全体で共有される1つのデータや処理として扱われます。

静的フィールドの宣言
static (型名) (フィールド名) = (初期値);
静的メソッドの宣言
static (戻り値の型) (メソッド名)([引数]) { 処理 }

サンプルプログラム

以下のサンプルを入力・実行してみてください。生成された Data オブジェクトの数を静的フィールドで管理しています。

プロジェクト: SampleEx201 / ファイル名: Data.cs
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);
        }
    }
}
プロジェクト: SampleEx201 / ファイル名: Program.cs
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();
            }
        }
    }
}
実行結果
Dataオブジェクトの数:0
値: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」と表示されます。

Dataクラスのインスタンスが生成される前の処理

図2-1. Dataクラスのインスタンスが生成される前の処理

図2-2: 最初のインスタンス(d[0])が生成されると、コンストラクタが呼ばれて num++ が実行され、num が 1 に増加します。静的フィールド num はクラスで唯一の変数なので、どのインスタンスからアクセスしても同じ値を参照できます。

Dataクラスのインスタンスが一つ生成されたときの状態

図2-2. Dataクラスのインスタンスが一つ生成されたときの状態

図2-3: 2つ目のインスタンス(d[1])が生成されると、再びコンストラクタが呼ばれ num が 2 に増加します。インスタンスフィールド id は各インスタンスごとに独立していますが、静的フィールド num はクラスに1つだけ存在し続けます。

Dataクラスのインスタンスの二つ目が生成されたときの状態

図2-3. Dataクラスのインスタンスの二つ目が生成されたときの状態

Main メソッド

Main メソッドは静的メソッド

ここで一度、これまで使ってきた Main メソッドについて振り返ってみましょう。Main メソッドは、プログラムの実行時に最初に呼び出される特殊なメソッドです。

Main メソッドの宣言を見ると、static void Main(string[] args) となっており、static が付いていることが分かります。つまり、Main メソッドは静的メソッドの一種なのです。プログラム起動時点ではインスタンスが存在しないため、エントリーポイントである Main メソッドは静的メソッドでなければなりません。なお、1つのプロジェクトに Main メソッドは1つしか置けません。

サンプルプログラム

以下のサンプルでは、Program クラスが自身のインスタンスを生成し、インスタンスメンバと静的メンバの両方にアクセスしています。

プロジェクト: SampleEx202 / ファイル名: Program.cs
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();
        }
    }
}
実行結果
pのインスタンスフィールド: inum = 200
Programのクラスフィールド: snum = 100
Fooメソッド(staticメソッド)
Barメソッド(インスタンスメソッド)

静的メンバとインスタンスメンバのアクセス規則

Main メソッドは静的メソッドなので、同じクラスの静的フィールド snum や静的メソッド Foo() にはそのままアクセスできます。一方、インスタンスメンバである inumBar() にアクセスするには、インスタンス p を生成してから p.inump.Bar() のようにアクセスする必要があります。

静的メンバとインスタンスメンバのアクセス規則
アクセス元静的メンバへのアクセスインスタンスメンバへのアクセス
静的メソッド○ 可能× 不可(インスタンス経由なら可)
インスタンスメソッド○ 可能○ 可能

練習問題(応用編)2 へ →