static

静的メンバー

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

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

静的メソッド

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


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の位置

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


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

静的フィールド

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


class SimpleClass
{
    //通常のフィールド
    public string Str;
    //静的フィールド
    public static string StaticStr;

    public SimpleClass(string s = "")
    {
        Str = s;
        StaticStr = s;
    }

    public void Print()
    {
        Console.WriteLine(Str);
        Console.WriteLine(StaticStr);
        Console.WriteLine();
    }
}

static void Main(string[] args)
{
    SimpleClass sc1 = new SimpleClass("abc");
    sc1.Print();

    SimpleClass sc2 = new SimpleClass("def");
    sc2.Print();

    sc1.Print();

    Console.ReadLine();
}
abc
abc

def
def

abc
def

インスタンスsc1の生成時には、文字列abcを静的フィールドを代入しています。
次にインスタンスsc2の生成時には、文字列defを静的フィールドを代入しています。
その後に再度インスタンスsc1の値を表示してみると、静的フィールドの値がsc2で代入したdefに書き換わっていることがわかります。

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


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

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

    //税込み価格を返す
    public int PriceIncludingTax()
    {
        return (int)(Price * Tax);
    }
}

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

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

    Console.WriteLine(sc1.PriceIncludingTax()); 
    Console.WriteLine(sc2.PriceIncludingTax());

    //静的フィールドへのアクセス
    Console.WriteLine(SimpleClass.Tax);

    Console.ReadLine();
}
108
540
1.08

静的フィールドの値はインスタンスではなくクラスが持つため、静的フィールドへのアクセスは「クラス名.静的フィールド名」という形で行います。
これは静的メソッドの場合と同じです。

静的メソッドと非静的メンバー

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


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にすることができます。
これを静的プロパティといいます。


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

プログラム中に「Mainという名前のstaticメソッドを持つクラス」が複数存在する場合、そのままではコンパイルエラーになります。
どのMainメソッドをプログラム開始点として使用するかを指定することでコンパイルできます。
(ややこしいのでそのような名前のメソッドは使用しないのが無難です)

静的コンストラクター

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


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

    public static string StaticStr2;

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

静的コンストラクターはstaticキーワードを付けて定義します。
アクセス修飾子(publicや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 インスタンス生成

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


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

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

//↓同じ意味

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