C#

最終更新日: 2022.12.20 (公開: 2022.12.20)

C#のstaticとは?静的クラス・静的メンバ・静的メソッドの活用法

C#のstaticとは?静的クラス・静的メンバ・静的メソッドの活用法

C#の「static」とは「静的な存在」を指す修飾子・キーワードで、主に静的クラスや静的メンバを作成するときに使用します。static修飾子を付けたクラスやメンバは、さまざまな点で通常のものとは異なり、特有の活用方法があるものです。重要なポイントは、static修飾子を付けたものは、「プログラム中で唯一の存在」になるということです。

staticにおける書き方自体は単純ですが、「どんなときに使えば良いかわからない」ことも多いでしょう。static修飾子を活用すると、今までより複雑な処理が簡単にできるようになるので、プログラミング上達のためにもぜひ覚えておきたい機能です。そこで本記事では、C#のstaticの使い方や応用テクニックについて、サンプルコード付きで解説します。

目次

C#の「static」とは?プログラム全体で唯一の存在

C#の「static」とは?プログラム全体で唯一の存在

C#の「static」とは、「静的な存在」を作り出すための修飾子・キーワードです。「静的」とは、インスタンス単位で生成されるものではなく、プログラム全体でただひとつだけ生成されるものを指します。つまり、staticキーワードを付けたクラスやメンバは「オンリーワンの存在」になるということです。

C#のstaticが活用されるのは、主に「静的クラス」や「静的メンバ」を作るときです。静的メンバには、「静的フィールド(静的メンバ変数や静的プロパティ)」と「静的メソッド(静的関数)」の2種類があるのをご存じでしょうか。静的メンバは静的クラス、もしくは通常のクラスに作成することができます。

静的クラスはインスタンス化できず、静的メンバはメソッドに紐づかないことが大きな特徴です。ちなみに、通常のメンバを静的メンバと明確に区別したい場合は、通常のメンバを「インスタンスメンバ(インスタンスフィールドやインスタンスメソッド)」と呼ぶこともあるので、覚えておきましょう。

そもそも「Mainメソッド」自体が静的メソッド!

「staticメソッドなんてわからない…」「難しそう」とお悩みの方も多いでしょう。しかし、staticメソッドはすでに何度も使っています。実はC#のプログラムで必ず書く「Mainメソッド」自体が、そもそもstaticメソッド(静的メソッド)なのです。以下のサンプルコードをご覧ください。

//サンプルプログラム

c_static

//実行結果

c_static

Mainメソッドの記述が「public static void Main()」となっていますが、これこそ「静的メソッド」です。この「static」を外すとどうなるでしょうか。実は以下のように、エラーが出てプログラムを実行できなくなるのです。

c_static

「エントリポイントに適切な静的Mainメソッドを含んでいません」とは、つまりMainメソッドにstatic修飾子が付いていないといけないということです。詳細は後述しますが、Mainメソッドはプログラム中でただ1度だけ実行すれば良いため、静的メソッドにするという決まりになっていることが理由です。

このように、static修飾子は今までとくに意識することなく使ってきましたが、さまざまな使い方や応用テクニックがあります。ここからは、C#のstaticの基本的な使い方やテクニックについて、より詳しく見ていきましょう。

C#のstaticメンバ(静的メンバ)の書き方・使い方

C#のstaticメンバ(静的メンバ)の書き方・使い方

C#のstaticメンバ(静的メンバ)の書き方・使い方について、以下4つの観点から解説します。

  • staticメンバ(静的メンバ)の基本的な書き方
  • クラス外からアクセスする場合は「クラス名」を指定する
  • 「静的プロパティ」でゲッターとセッターも作成できる
  • staticフィールドの初期化は「静的コンストラクタ」で行う

staticメンバ(静的メンバ)の基本的な書き方

C#でstaticメンバ(フィールドとメソッド)を作るための、基本的な構文は以下のとおりです。

c_static

基本的な書き方は通常のメンバと変わらず、「public」や「private」などのアクセス修飾子のあとに「static」を書くだけです。たとえば「public static void Test (int num)」のように書くと、Testメソッドが静的メソッドになります。なお「static public」のように、逆順で書いても構いませんが、プログラム全体で統一するほうが見やすいので、基本的には「public static」と書くほうが良いでしょう。

staticメンバを同じクラス内から呼び出す場合は、通常どおりフィールドやメソッドの名称を記載するだけで大丈夫です。以下のサンプルプログラムで、staticメンバの作り方とアクセス方法を確認しておきましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記はとくに何もしていないプログラムですが、これがC#のstaticの基礎となる重要な部分です。まずはここから覚えていきましょう。

クラス外からアクセスする場合は「クラス名」を指定する

staticメンバにクラス外からアクセスする場合は、「クラス名」を直接指定してアクセスします。通常のメンバは「インスタンス」からアクセスしますが、staticメンバはインスタンスとは紐づいていないことが理由です。構文は以下のとおりです。

c_static

言い換えれば、staticメンバには「インスタンスなしでアクセスできる」ということ、つまり「どこからでもアクセスできる」ということです。たとえば数学関連のメソッドのように、インスタンスを作る必要がないものや、どこからでもアクセスできる必要があるものは、以下のようにstaticメソッドとして定義することが一般的です。

//サンプルプログラム

c_static

//実行結果

c_static

実は普段から当たり前のように使ってきた「WriteLineメソッド」も、staticメソッドです。WriteLineメソッドは、「Consoleクラス」に属する静的メソッドなので、インスタンスなしでどこからでもアクセスできることがポイントです。

もしWriteLineが静的メソッドでなければ、コンソール画面に文字を表示するときに、いちいちConsoleクラスをインスタンス化しないといけません。このようにC#のstaticメンバは、「よく使うメソッドをどこでも使えるようにする」ために、極めて便利な機能なのです。

「静的プロパティ」でゲッターとセッターも作成できる

C#のクラスでは「プロパティ」もstaticにできます。そもそもプロパティとは、クラスのメンバ変数が手軽に使えるようになる便利機能です。クラス外部からはメンバ変数に直接アクセスできるように見えるうえに、プロパティ内部ではメソッドのように値の代入や提供ができます。以下のように、このプロパティにもstaticを付けることができます。

c_static

静的プロパティの基本的な性質は通常のプロパティと同じですが、インスタンスに紐づいていないため、外部からアクセスする場合は「クラス名」を指定します。詳細は以下のサンプルプログラムのとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

ちなみに、プロパティから「set;」の部分を取り除くと、そのプロパティは「リードオンリー」つまり読み込み専用になります。クラス外部からは値の書き換えができなくなるので、より安全なプログラミングが可能です。

staticフィールドの初期化は「静的コンストラクタ」で行う

staticフィールドの初期化は、「静的コンストラクタ」という特別なコンストラクタで行います。静的コンストラクタには、任意の静的フィールドを初期化する役割があり、構文は以下のとおりです。

c_static

静的コンストラクタは、通常のコンストラクタとは以下3つの点で異なるので、使用するときは注意が必要です。

  • アクセス修飾子を付けてはならない
  • 引数を持ってはならない
  • 静的コンストラクタは自動的に呼び出される

静的コンストラクタには、「public」や「private」のようなアクセス修飾子は付けられません。なぜなら静的コンストラクタには、プログラマーが手動でアクセスすることがないからです。同じ理由で、静的コンストラクタは引数を持てません。

なお静的コンストラクタは、静的メンバが参照されたとき、もしくは最初にインスタンスが生成されたときに実行されます。詳細を以下のサンプルコードで確認しましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、TestClassの静的フィールド「message」を、静的コンストラクタで初期化しています。さらに各所でメッセージを表示し、どのタイミングで静的コンストラクタが呼び出されるか可視化しました。そのクラスにアクセスしたタイミングで静的コンストラクタが呼ばれ、通常のコンストラクタより早い段階で呼び出されます。

C#のstaticクラス(静的クラス)の書き方・使い方

C#のstaticクラス(静的クラス)の書き方・使い方

static修飾子は、フィールドやメソッドなどのメンバだけではなく、クラスそのものに付けることも可能です。以下の構文のように、クラス定義時にstatic修飾子を付けると、「staticクラス(静的クラス)」が作れます。

c_static

静的クラスの大きな特徴は、静的メンバしか持つことができず、インスタンスの生成もできないことです。たとえば標準ライブラリでは、数学演算を行う「Mathクラス」が静的クラスとして定義されています。インスタンスの生成ができず、どこからでもアクセスできるという特性は、便利機能をまとめた「ユーティリティクラス」を実装したいときに便利です。

ちなみにコンストラクタをprivateにすると、通常のクラスでもインスタンスを生成できないので、staticクラスの代用として使用されることがあります。しかし、それではクラスの目的が曖昧になってしまうでしょう。インスタンスを生成させたくない場合は、積極的に静的クラスを作成します。静的クラスの作り方・使い方は以下のとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

static修飾子が付いた「静的クラス」や「静的メンバ」の特徴

static修飾子が付いた「静的クラス」や「静的メンバ」の特徴

static修飾子が付いた「静的クラス」や「静的メンバ」の特徴について、以下6つの観点から詳しく解説します。

  • 静的メンバはインスタンスに紐づかない
  • 静的クラスはインスタンス化できない
  • 静的メンバにはどこからでもアクセスできる
  • 静的メンバは継承先からもアクセスできる
  • 静的クラスは継承できない
  • 静的メンバはオーバーライドできる

静的メンバはインスタンスに紐づかない

静的メンバ(静的フィールドや静的メソッド)は、インスタンスと紐づいていません。そのため、静的メンバは「メンバ」という名称は持ちながらも、静的メソッドから通常のインスタンスメンバにはアクセスできません。詳細を以下のサンプルプログラムで確認しましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、インスタンスメンバと静的メンバの関連性を検証しています。インスタンスメンバはインスタンスと紐づき、静的メンバはクラス自体と紐づいているため、アクセス可能・不可能なものに違いがあります。コメントアウトしている部分を元に戻すと、コンパイルエラーとなるのでご確認ください。

静的クラスはインスタンス化できない

静的クラスはインスタンス化できません。そのため、インスタンス化する必要があるクラスは、通常どおりstaticを付けないようにしましょう。サンプルコードは以下のとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、静的クラスをインスタンス化しようとすると、「インスタンスは作成できません」という内容のコンパイルエラーが表示されます。つまり、静的クラスは「オブジェクト」というよりは、グローバルに使える変数やメソッドの「集合体」だといえるでしょう。

静的メンバにはどこからでもアクセスできる

メンバのアクセス修飾子が「public」になっている限り、静的メンバにはクラス内・クラス外を問わずどこからでもアクセスできます。詳細は以下のサンプルコードのとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、それぞれアクセス修飾子が異なる複数の静的メンバを用意して、外部からアクセスできるかどうかチェックしています。アクセス修飾子がpublicでなければ、クラス外からアクセスできないという点は通常のメンバと同じです。

静的メンバは継承先からもアクセスできる

静的メンバには、そのメンバが所属するクラスを継承したクラスからでも、アクセスすることができます。静的メンバとスーパークラス・サブクラスの関連性を、以下のサンプルコードで検証してみましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、親クラスに存在する静的メンバには、子クラスはもちろん外部クラスからもアクセスできます。さらに子クラスを介して、親クラスの静的メンバにアクセスすることも可能です。ただし、そもそも親クラスの静的メソッドのアクセス修飾子が「private」になっている場合は、子クラスからも外部クラスからもアクセスできないので注意してください。

静的クラスは継承できない

C#では、静的クラスはいかなる場合でも継承できません。詳細は以下のサンプルコードで検証してみましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、静的クラスを派生させようとすると、コンパイルエラーが出ます。「静的クラスはオブジェクトから派生する必要があります」とは、「Objectクラスから派生させたクラスはstaticにできる」ということです。詳細は以下のサンプルコードのとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

以上のサンプルプログラムのように、Object型以外のクラスを派生させた場合、そのクラスをstaticにすることはできません。そもそも、クラスの派生は「ポリモーフィズム(多様性)」を実現するための機能です。プログラム中でひとつしか存在できない静的クラスでは、ポリモーフィズムは実現できないため、静的クラスを派生させる必要はないのです。

静的メンバはオーバーライドできる

静的クラスはいっさい派生させられませんが、静的メンバを持つ通常クラスを派生させることは可能です。その際、派生先のクラスでも静的メンバにはアクセスできますが、静的メソッドは全体でひとつなので、常に同じ値を共有することになります。以下のサンプルコードで確認してみましょう。

//サンプルプログラム

c_static

//実行結果

c_static

静的メンバを含む通常クラスは、オーバーライドもできます。ただしオーバーライドした場合は、通常のインスタンスメンバ同様に、スーパークラスのメンバは隠蔽されるので注意してください。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、親クラスと子クラスではフィールドの値が異なっています。つまり親クラスと子クラスでは、それぞれ個別の静的メンバを作成しているということになります。これは通常メンバを継承した場合と同じ挙動ですが、静的メンバの場合も同じなので、クラスの継承時は注意してください。

C#のstaticの発展的な機能やテクニックを2つ紹介

C#のstaticの発展的な機能やテクニックを2つ紹介

C#のstaticの発展的な機能やテクニックについて、以下2つをご紹介します。

  • 拡張メソッドで静的メソッドがさらに快適に使える
  • 「using static」で静的メソッド呼び出し時のクラス名を省略できる

拡張メソッドで静的メソッドがさらに快適に使える

C#の「拡張メソッド」は、静的メソッドの書式を変更し、あたかもインスタントメソッドであるかのように呼び出せる機能です。なかなかイメージしにくい機能ではありますが、具体的には以下のような記法で、クラスの静的メソッドが呼び出せるようになります。

c_static

たとえば、StringUtilityというクラスにstring型の引数strを取る「ToggleCaseメソッド」がある場合、通常は「StringUtility.ToggleCase (str)」と呼び出す必要があります。しかし、拡張メソッドを使えば「str.ToggleCase()」と呼び出せることになり、あたかも「string型のインスタンスメソッドを呼び出した」ように見えるのです。

ただし、これは既存クラスにインスタンスメソッドを実際に追加できる機能ではなく、あくまで「インスタンスメソッドのように呼び出せる」というだけです。また、拡張メソッドが使えるようにするためには、以下の構文でメソッドを定義する必要があります。

c_static

基本的には通常のstaticメソッドと同じですが、引数の型の前に「this」を付ける必要があります。言い換えれば、これまでご紹介した静的メソッドの引数にthisを付けるだけで、拡張メソッドの便利機能を利用できるということです。拡張メソッドの基本的な使い方は、以下のサンプルコードのとおりになります。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、オリジナルのユーティリティクラス「StringUtility」に、大文字と小文字を入れ替えるためのメソッドを追加したものです。通常は「StringUtility.ToggleCase(before);」と、静的メソッドの呼び出し構文を使用しないといけません。

しかし拡張メソッドの機能を使うと、「before.ToggleCase();」で呼び出せるのです。これは、あたかもstringクラスに「ToggleCaseメソッド」があるかのような呼び出し方です。拡張メソッドを利用することにより、あたかも既存クラスにインスタンスメソッドを追加したときのように使えます。

さらに、通常のクラスだけではなく「インターフェース」に対しても、同じように拡張メソッドを利用可能。ちなみに、Visual Studioの予測表示にも、この機能を使ったメソッドがきちんと表示されます。

c_static

ただし、実際にstringクラスに独自メソッドを追加できるわけではないので、拡張メソッドの利用時はその点に注意してください。

「using static」で静的メソッド呼び出し時のクラス名を省略できる

「using static」は、静的メソッドの呼び出し時にクラス名を省略できるようになる機能です。クラスにアクセスするとき、通常は「System.Console」のように「名前空間」を指定する必要があります。しかし「using staticディレクティブ」を使うと、名前空間を書かなくて良いので便利です。

これを静的メソッドにまで拡張できるのが、using staticディレクティブです。以下の構文でクラス名に対してusing staticディレクティブを使うと、静的メソッドの呼び出し時にクラス名を書く必要もありません。

c_static

いわば名前空間名を省略するときに使う、「Usingディレクティブ」のstaticメソッド版のようなものです。using staticディレクティブでは、クラス名を指定しなくても、あたかもローカルなメソッドのように呼び出せます。using staticディレクティブの使い方は、以下のサンプルコードのとおりです。

//サンプルプログラム

c_static

//実行結果

c_static

前述したように、「WriteLineメソッド」は標準ライブラリの静的メソッドなので、using staticディレクティブでクラス名を省略できます。ちなみにC#の列挙型「enum」も内部的には静的メンバなので、以下のように列挙型の名称も省略できるようになります。

//サンプルプログラム

c_static

//実行結果

c_static

列挙体の要素を指定するとき、列挙体そのものの名前を指定するのは面倒ですし、ソースコードが長くなってしまいます。そのため、列挙体に対してusing staticディレクティブを使うケースは比較的多くあります。

C#のstaticの使いどころや応用テクニック4選

C#のstaticの使いどころや応用テクニック4選

C#のstaticの使いどころや応用テクニックについて、以下4つを詳しく解説します。実際にC#でプログラミングを行うにあたり、非常に便利な知識・テクニックなのでぜひご活用ください。

  • インスタンス生成数をカウント
  • ユーティリティクラス
  • ファクトリーパターン(ファクトリーメソッド)
  • シングルトンパターン

インスタンス生成数をカウント

C#のstatic修飾子は「現在のインスタンス数」をカウントしたいときに使われることが多いです。現在のプレイヤー数やユーザー数をカウントしたいときは、通常のメンバ変数にすると、インスタンスごとに値が異なるため機能を果たしません。そこで静的メンバにすることで、すべてのインスタンス共通の値を保持できるため、全体数を把握できる点がメリットです。実装例を以下のサンプルコードでご紹介します。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、ユーザーが入力したキーに応じて、プレイヤー数を増減させます。ユーザーが「a」を入力すれば追加、「d」なら削除というように、リアルタイムでユーザー数を反映させます。このテクニックはさまざまな場面で役に立つでしょう。たとえば、IDとユーザー名を自動的に生成したい場合も、静的メンバを用意すれば簡単に実装できます。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、ユーザーを追加するときに、現在の総インスタンス数を基にして自動的にIDとユーザー名を生成しています。ユーザー数を正しく反映できるだけではなく、仮のデータを自動的に作れるので便利です。

ユーティリティクラス

「ユーティリティクラス」は、プログラム中でよく使用する共通機能を提供するためのクラスで、通常は静的クラスとして実装されます。前述したように、C#の標準機能である「Consoleクラス」や「Mathクラス」も、このユーティリティクラスに該当します。ユーティリティクラスは、以下のようにさまざまな場面で活用できる便利な機能です。

//サンプルプログラム

c_static

//実行結果

c_static

ちなみに、ユーティリティクラスはオブジェクト指向的ではないため、「使うべきではない」という論調は根強いです。しかしながら、C#の標準ライブラリがユーティリティクラスを提供している以上、ユーティリティクラスを排除するというのは不適切です。

そもそもC#は完全なオブジェクト指向言語ではなく、C言語やC++の「手続き型言語」としての特性も兼ね備え、多様なプログラミング手法を認めている言語です。オブジェクト指向は「絶対的」なものではなく、あくまで「ひとつの選択肢」にしか過ぎません。

したがってこのユーティリティクラスも、「必要に応じて使いこなす」という方法が、C#を学習・活用するうえで正しいです。プログラマーとは、常にあらゆるオプションをテーブルに残し、その時々で最適なディシジョンを下すべき存在でもあります。

ファクトリーパターン(ファクトリーメソッド)

「ファクトリーパターン(ファクトリーメソッド)」とは、インスタンスの生成をメソッドで行い、生成するクラスを「文字列」など間接的な要素で指定するテクニックです。通常、インスタンスの生成は、インスタンスが必要な場面でnew演算子を使用して、その際にクラス名を直接指定します。

しかしファクトリーパターンでは、インスタンスの生成は内部のメソッドに委ねて、メソッドからインスタンスを取得します。さらに、クラス名を直接指定するのではなく、たとえば「文字列」「列挙体」「ID」など間接的な形式で指定するのです。難しく感じてしまうかもしれませんが、要は以下のようなメソッドが「ファクトリーメソッド」になります。

c_static

要するに、引数で与えられた識別子に応じて、該当するオブジェクトを生成するメソッドが「ファクトリーメソッド」であり、そのメソッドを基軸としてオブジェクトを運用するのが「ファクトリーパターン」なのです。実装例を以下のサンプルコードで確認しましょう。

//サンプルプログラム

c_static

//実行結果

c_static

ファクトリーメソッドの流れ自体は、先ほどご紹介したとおりです。オブジェクトを生成する「ObjectFactoryクラス」が、静的クラスになっていることがポイントです。これは、ObjectFactoryはただひとつだけ存在すべきで、外部でインスタンスを生成されたくないことが理由です。

ObjectFactoryが生成するクラスが、「BaseObject抽象クラス」とそれを継承したサブクラス群です。それらのインスタンス生成時に、クラス名を指定する必要がなく、Mainメソッド内に「new」でインスタンスを生成するシーンがひとつもありません。これは、外部ファイルからオブジェクトの生成を制御するときに、大きなアドバンテージとなります。

たとえばゲームプログラミングで、外部ファイルに記載したスクリプトで、オブジェクトを出現させるような場合です。下記のサンプルコードで確認するために、まずCドライブ直下に「Test」というフォルダを作り、以下のような「スクリプトファイル」を「Script.csv」と名前を付けて保存してください。

//スクリプトファイル「Script.csv」

c_static

そのうえで以下のサンプルプログラムを実行してみましょう。非常に長いソースコードではありますが、そのままゲームプログラミングで使える構成になっているので、ぜひ参考にしてみてください。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、指定したタイミングと座標値に従って、リアルタイムでオブジェクトを出現させることができるでしょう。こういった「スクリプト」を活用したプログラムは、ゲームプログラミングでよく使われます。

重要なポイントは、ファクトリーメソッドを使用しているからこそ、外部ファイルから自由にオブジェクトの出現ができるということです。スクリプトファイルは、構文さえ覚えれば、プログラマーでなくてもゲームステージを構成できますし、変更時に再コンパイルも必要ありません。そのため、実際のゲーム開発現場でも広く活用されているテクニックです。

シングルトンパターン

「シングルトンパターン」は、インスタンスが「ただひとつしか生成されない」ことを保証する仕組みのことです。複数のインスタンスの生成を禁止することにより、想定外のバグが発生してしまうリスクを防げます。ただし、以下3つの項目すべてを満たしていることが、シングルトンパターンであることの条件です。

  • 「private」のコンストラクタを持つ
  • 静的なprivate変数で「自身のクラスインスタンス」を持つ
  • 静的なpublic関数で「自身のクラスインスタンスを返すメソッド」を持つ

実際に以下のサンプルコードで、シングルトンパターンの概要を確認してみましょう。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、簡易的なシングルトンクラスを作成し、インスタンスを2つ取得したあとで、両者が同じインスタンスであるかを調べるためのものです。その結果、両者は同じインスタンスであり、シングルトンクラスではひとつのインスタンスしか生成できないことがわかりました。自身のインスタンスをprivateメンバ、インスタンスのゲッターをpublicメンバとして持ち、いずれも「静的な(staticな)」ものにすることが重要です。

「スレッドセーフ」なシングルトンクラスの作り方

先ほどのサンプルプログラムは、「スレッドセーフではない」ことが大きな問題になります。スレッドセーフとは、マルチスレッド処理を行ったときに、スレッド同士の競合が起きないことです。たとえば以下のように、複数のスレッドからGetInstanceメソッドを呼び出すと、インスタンスが複数作成されてしまいます。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、Singletonクラスのコンストラクタが8回も呼ばれてしまっています。なお実行結果は、パソコンの仕様によって異なる場合があります。これをスレッドセーフにするためには、以下のように「静的コンストラクタ」でインスタンスを生成するのが現実的です。

//サンプルプログラム

c_static

//実行結果

c_static

上記のように、見事に1回しかコンストラクタが呼ばれなくなりました。しかし、C#の静的コンストラクタは、初めてクラスにアクセスされたときに呼び出されます。そのため、同時にアクセスしたときは、「やはり競合が発生してしまうのでは」と心配になるかもしれません。

実は、C#の静的オブジェクトの初期化はスレッドセーフであることが保証されるため、これだけで「スレッドセーフなシングルトンクラス」が作れます。なお、以下のように記載すると、よりシンプルなソースコードになります。

//サンプルプログラム

c_static

自身のインスタンスをプロパティで持っているため、ゲッターが不要となり、さらに初期値としてあらかじめインスタンスを生成しているため、静的コンストラクタも不要です。ちなみに、上記のプログラムもスレッドセーフであることが保障されています。

シングルトンパターンの活用例

シングルトンパターンは、「ひとつしか存在しないオブジェクト」に対して使うのが理想的です。たとえばゲームプログラミングでは、ゲーム全体を管理する「ゲームマネージャー」がひとつだけ存在すれば十分です。インスタンスが複数あると予期せぬエラーやバグの原因になるので、シングルトンパターンを活用すれば、バグのリスクを効果的に減らしやすくなります。

//サンプルプログラム

c_static

//実行結果

c_static

上記のサンプルプログラムでは、「GameManagerクラス」をシングルトンパターンで実装しています。ゲーム全体をコントロールするため、インスタンスを作成されてはバグの原因になるでしょう。そのうえで、マルチスレッドでオブジェクトの更新・描画を実行しています。

なお、メソッド内で使用している「Lock」という構文は、マルチスレッド処理時に他スレッドとの「競合」を防ぐためのものです。Lockブロック内の処理は、他スレッドの実行中は待機し、他スレッドの処理が終了してから実行されます。なお実行結果は毎回異なりますが、これはスレッドの実行タイミングがそのときによって変わることが理由です。

C#のstatic(静的クラス・静的メンバ)を使いこなそう!

C#のstatic(静的クラス・静的メンバ)を使いこなそう!

C#のstatic(静的クラス・静的メンバ)は、インスタンスに属さず、クラスそのものに属するメンバ(フィールドやメソッド)を作成するための機能です。static修飾子を付けたメンバは、すべてのインスタンスで共通の値を保持できることや、外部クラスからインスタンスなしでアクセスできることが特徴です。

staticには、「ユーティリティクラス」「ファクトリーパターン」「シングルトンパターン」など、さまざまな活用方法・応用テクニックがあります。いずれも実際のプログラム開発でよく活用されるので、少しずつ覚えていくとより高度なプログラミングができるようになります。今回ご紹介した知識やテクニックを参考にして、C#のstaticを活用できるようにしましょう

アクセスランキング 人気のある記事をピックアップ!

    コードカキタイがオススメする記事!

    1. 子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

      2024.01.26

      子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

      #プログラミングスクール

    2. 【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

      2022.01.06

      【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

      #プログラミングスクール

    3. 【未経験でも転職可】30代におすすめプログラミングスクール8選!

      2024.01.26

      【未経験でも転職可】30代におすすめプログラミングスクール8選!

      #プログラミングスクール

    4. 初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

      2024.01.26

      初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

      #JAVA

    5. 忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

      2024.01.26

      忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

      #プログラミングスクール

    1. 【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

      2022.01.06

      【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

      #プログラミングスクール

    2. 【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

      2024.01.26

      【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

      #プログラミングスクール

    3. 【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

      2024.01.26

      【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

      #プログラミングスクール

    4. 【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

      2024.01.26

      【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

      #プログラミングスクール

    5. 【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

      2024.01.26

      【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

      #プログラミングスクール