static

静的メンバー

クラスの内部にはフィールド、メソッド、プロパティなどのメンバーを持つことができます。
クラスはインスタンス化して使用し、それぞれのインスタンスで異なる値を保持できます。

これに対して、インスタンスではなくクラスに依存するメンバーを定義できます。
これを静的メンバーといいます。

静的メソッド

クラスの基礎などでは、メソッド(クラス内関数)の定義時には先頭にstaticを付けない、とだけ説明しました。
メソッドにstaticキーワードを付けると、そのメソッドは静的メソッド(staticメソッド)となります。
静的メソッドはインスタンスに依存しないメソッドとなります。


private class SimpleClass
{
    //通常のメソッド
    public void Test()
    {
        Console.WriteLine("Testメソッド");
    }

    //静的メソッド
    public static void StaticTest()
    {
        Console.WriteLine("StaticTestメソッド");
    }
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();

    //通常のメソッド呼び出し
    sc.Test();

    //静的メソッド呼び出し
    SimpleClass.StaticTest();

    Console.ReadLine();
}
Testメソッド
StaticTestメソッド

通常のメソッドの呼び出しは、まずクラスのインスタンスを生成し「インスタンス名.メソッド名」という形で呼び出します。
静的メソッドは「クラス名.メソッド名」という形で呼び出します。
インスタンスを必要としないので呼び出しが簡単です。

通常のメソッドと静的メソッドは単に呼び出し方の違いというわけではありません。
静的メソッドは静的メンバーにしかアクセスできないという制限があります。
(後述)

staticの位置

staticとアクセス修飾子の順序に決まりはなく、どちらから書いても構いません。
ただ混在させるとややこしいので統一しておきましょう。


private class SimpleClass
{
    public static void StaticTest1() { }
    static public void StaticTest2() { }
}

静的フィールド

staticはフィールドに付けることもできます。
これは静的フィールドとなり、インスタンスに依存しないフィールドとなります。


private class SimpleClass
{
    //通常のフィールド
    public string Str = "normal";

    //静的フィールド
    public static string StaticStr = "static";
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();

    //通常のフィールド
    Console.WriteLine(sc.Str);

    //静的フィールド
    Console.WriteLine(SimpleClass.StaticStr);

    Console.ReadLine();
}

静的フィールドはクラスに依存する変数です。
インスタンスに依存しないということは、複数のインスタンスで共通の値になるということです。
クラスやインスタンス全体で常に同一になるデータはstaticにしておくことができます。


private class SimpleClass
{
    //通常のフィールド
    //商品の価格
    public int Price;

    //静的フィールド
    //税率は商品全体で同じ
    public static float Tax;

    public void Write()
    {
        Console.WriteLine(Price * Tax);
    }
}

static void Main(string[] args)
{
    SimpleClass.Tax = 1.08f;

    SimpleClass sc1 = new SimpleClass();
    SimpleClass sc2 = new SimpleClass();

    sc1.Price = 100;
    sc2.price = 500;            

    sc1.Write();
    sc2.Write();

    Console.ReadLine();
}
108
540

静的メソッドと非静的フィールド

静的メソッドからは非静的なメンバーにアクセスすることはできません。


private class SimpleClass
{
    //通常のフィールド
    string str = "normal";

    //静的フィールド
    static string staticStr = "static";

    //通常のメソッド
    public void Test1()
    {
        Console.WriteLine(str);
        Console.WriteLine(staticStr);
        Test2();
        StaticTest2();
    }
    public void Test2() { }            

    //静的メソッド
    public static void StaticTest()
    {
        Console.WriteLine(str); //エラー
        Console.WriteLine(staticStr);
        Test2(); //エラー
        StaticTest2();
    }
    public static void StaticTest2() { }
}

静的メソッドはクラスに依存するため、インスタンスの生成が必要ありません。
静的メソッド側からすれば、プログラム上に存在しないかもしれないインスタンスにアクセスできませんし、存在していたとしてもインスタンスは無数に作られるためどのインスタンスのデータを使用すれば良いか判断できません。

同様の理由で静的メソッド内ではthisキーワードは使用できません。

通常のメソッドから静的メンバーにアクセスすることは問題ありません。
静的メンバーは、インスタンスをいくつ作ろうともプログラム全体で常にひとつしか存在しないので上記のような問題がないためです。

静的メソッド内で必要な値はすべて引数で渡すか、静的フィールド(または静的プロパティ)で持っておく必要があります。
逆にいえばそれが可能なメソッドは静的メソッドにしておけば、インスタンスを生成することなくメソッドを実行できます。
例えばMathクラスのメソッドはすべて静的メソッドで提供されています。

静的プロパティ

プロパティもstaticにすることができます。
これを静的プロパティといいます。


private class SimpleClass
{
    public string Str { get; set; }

    //静的プロパティ
    public static string StaticStr { get; set; }
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();

    sc.Str = "abc";
    SimpleClass.StaticStr = "def";

    Console.ReadLine();
}

性質については静的フィールドや静的プロパティと同じです。

呼称について

staticキーワードを付けたメンバーは静的メンバーといいます。
その他staticメンバーや、クラスに依存するのでクラスメンバーと呼んだりもします。

それに対して通常のメンバーはインスタンスに依存するのでインスタンスメンバーと呼びます。

staticなフィールド、プロパティ、メソッドはそれぞれクラスフィールドクラスプロパティクラスメソッドと呼びます。
通常のフィールド、プロパティ、メソッドはそれぞれインスタンスフィールドインスタンスプロパティインスタンスメソッドと呼びます。

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

ここまでの説明でわかると思いますが、Mainメソッドは静的メソッドで定義されています。
これは「Mainという名称のstaticメソッド」からプログラムが開始されるというC#のルールです。
(そのため「Mainという名称のstaticメソッド」はプログラム全体でひとつしか定義できません)

Mainメソッドが静的メソッドなので、Mainメソッドが定義されているクラス内の他のメソッドやフィールドなどはstaticにしておかないとMainメソッド内から使用できません。
今までメソッドを定義する場合にすべてstaticにしていたのはこれが理由です。

静的コンストラクター

静的フィールドはインスタンスを生成することなく使用できるので、通常のコンストラクターでは初期化ができません。
(コンストラクターはインスタンスを生成する際に実行されるため)
静的フィールドを初期化するには静的コンストラクターを使用します。


private class SimpleClass
{
    //初期化子で初期化
    public static string StaticStr1 = "abc";

    public static string StaticStr2;

    //静的コンストラクター
    static SimpleClass()
    {
        //静的コンストラクターで初期化
        StaticStr2 = "def";
    }
}

静的コンストラクターはstaticキーワードを付けて定義します。
アクセス修飾子(publicやprivateなど)は付けることはできません。
静的コンストラクターは外部から呼び出されることはないからです。
同じ理由で、静的コンストラクターに引数を付けることはできません。

静的コンストラクターが実行されるタイミングは、最初にそのクラスにアクセスした時です。


private class SimpleClass
{
    public static int Num;

    static SimpleClass()
    {
        Console.WriteLine("SimpleClass 静的コンストラクター実行");
        Num = 10;
    }

    public SimpleClass()
    {
        Console.WriteLine("SimpleClass インスタンス生成");
    }
}

static void Main(string[] args)
{
    Console.WriteLine("Mainメソッド開始");
    Console.WriteLine(SimpleClass.Num);
    SimpleClass sc = new SimpleClass();            

    Console.ReadLine();
}
Mainメソッド開始
SimpleClass 静的コンストラクター実行
10
SimpleClass インスタンス生成

静的フィールドは初期化子で初期化することもできます。
これは静的コンストラクターで初期化したものとみなされます。


private class SimpleClass
{
    public static string StaticStr1 = "abc";
    public static string StaticStr2;

    static SimpleClass()
    {
        StaticStr2 = "def";
    }
}

//↓同じ意味

private class SimpleClass
{
    public static string StaticStr1;
    public static string StaticStr2;

    static SimpleClass()
    {
        StaticStr1 = "abc";
        StaticStr2 = "def";
    }
}

静的クラス

staticはクラスのメンバーだけでなくクラス自身に付けることもできます。
これを静的クラス(staticクラス)といいます。


//静的クラス
static class StaticClass
{
    //メンバーはすべて静的メンバー

    static int num;
    static void Speak()
    {
        Console.WriteLine("Hello");
    }

    //インスタンスメンバーは使用できない
    //int num2;
}

静的クラスは

  • 静的メンバー以外は持てない
  • インスタンスを生成できない

というクラスになります。

同じ例になってしまいますが、Mathクラスは静的クラスとして定義されており、Mathクラスはインスタンスを生成することはできません。

「インスタンスを生成できないクラス」というだけならばコンストラクターをprivateにするだけでも実現できますが、そのようなクラスにインスタンスメンバー(非静的メンバー)を定義してもアクセスする方法がなく無意味になります。
それならば最初から静的メンバーしか定義できないクラスがあったほうが良いので静的クラスの機能が提供されています。