静的コンストラクターはどのように機能しますか?


82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

これが私が想定したシーケンスです

  1. 静的コンストラクターの開始
  2. 静的コンストラクターの終了
  3. メインの開始
  4. MyMethodの開始
  5. メインの終わり

どんなシナリオでも、4が2の前に開始する場合、私は失敗します。出来ますか?


8
これはJavaまたはC#の質問ですか?あなたは両方のタグを付けましたが、仕様は2つの言語で同じではないと思います。
ARRG 2012

私のオープニングでは、これは両方で同じように機能します。しかし、私はC#の男です。そのためにSry
om471987 2012

4
Javaには、同じように静的コンストラクターはなく、静的初期化用の静的ブロックだけがあります。static {//何かをする...}
deraj 2012

2
個人的には、静的コンストラクター内での対話性の形式に不快感を覚えます。私はあなたの目標を理解しています(この静的クラスのすべてのメソッドを、ユーザーが許可されるまで待機させてから実行を許可します)が、それを達成するこのメソッドは本当に嫌いです。
ブライアン

@Brian:-ええ...そうです...私はちょうど分析を行っていました..最後に、コンストラクターではなく初期化メソッドを使用することにしました
om471987 2012年

回答:


220

ここでは1つだけ質問しましたが、質問する必要がある質問が12ほどあるので、すべて答えます。

これが私が想定したシーケンスです

  1. クラスコンストラクターの開始(別名cctor
  2. cctorの終わり
  3. メインの開始
  4. MyMethodの開始

これは正しいです?

いいえ。正しい順序は次のとおりです。

  1. プログラムのcctorがあれば、それを開始します。存在しない。
  2. プログラムのcctorがあれば、それを終了します。存在しない。
  3. メインの開始
  4. MyClassのcctorの開始
  5. MyClassのcctorの終わり
  6. MyClass.MyMethodの開始

静的フィールド初期化子がある場合はどうなりますか?

CLRは、静的フィールド初期化子が実行される順序を変更できる場合があります。詳細については、この件に関するJonのページを参照してください。

静的コンストラクターと型初期化子の違い

MyMethodそのクラスのcctorが完了する前に、のような静的メソッドが呼び出される可能性はありますか?

はい。cctor自体がMyMethodを呼び出す場合、明らかに、cctorが完了する前にMyMethodが呼び出されます。

cctorはMyMethodを呼び出しません。MyMethodMyClassのcctorが完了する前に、のような静的メソッドが呼び出される可能性はありますか?

はい。cctorがMyMethodを呼び出す別のタイプをcctorが使用する場合、MyClasscctorが完了する前にMyMethodが呼び出されます。

直接または間接的に、MyMethodを呼び出すcctorはありません。MyMethodMyClassのcctorが完了する前に、のような静的メソッドを呼び出すことは可能ですか?

番号。

複数のスレッドが関係している場合でも、それはまだ本当ですか?

はい。cctorは、静的メソッドを任意のスレッドで呼び出す前に、1つのスレッドで終了します。

cctorを複数回呼び出すことはできますか?2つのスレッドの両方がcctorを実行するとします。

関係するスレッドの数に関係なく、cctorは最大で1回呼び出されることが保証されています。2つのスレッドがMyMethodを「同時に」呼び出すと、それらは競合します。そのうちの1人はレースに負け、MyClasscctorが勝利スレッドで完了するまでブロックします。

cctorが完了するまで、失われたスレッドはブロックされますか?本当に

本当に。

では、勝ったスレッドのcctorが、負けたスレッドが以前に取得したロックをブロックするコードを呼び出した場合はどうなるでしょうか。

次に、古典的なロック順序の反転条件があります。プログラムがデッドロックします。永遠に。

それは危険なようです。どうすればデッドロックを回避できますか?

あなたがそれをするときにそれが痛いなら、それからそれをやめなさい。cctorでブロックできるようなことは絶対にしないでください。

複雑なセキュリティ要件を適用するためにcctor初期化セマンティクスに依存することは良い考えですか?そして、ユーザーとの対話を行うcctorを用意するのは良い考えですか?

どちらも良い考えではありません。私のアドバイスは、メソッドのセキュリティに影響を与える前提条件が満たされていることを確認するための別の方法を見つける必要があるということです。


5
エリック、なぜこの回答で「静的コンストラクター」を「クラスコンストラクター」または「cctor」に置き換えたのか興味があります。cctorを参照するときに「静的コンストラクター」を使用することは不適切ですか?
phoog 2012

6
@phoog:用語の使用に一貫性を持たせたかったので、最も短い用語を選びました。「静的コンストラクター」と「クラスコンストラクター」はどちらも問題ありません。実装の詳細として、型の静的コンストラクターは「.cctor」と呼ばれる特別なメソッドとして発行されるため、このようなコンストラクターを「cctor」と呼ぶのが一般的です。もっと正式な文脈で書いていたら、もっと長い用語の1つを使います。
Eric Lippert

@EricLippertこれは、静的コンストラクターを持つ非静的クラスにも当てはまりますか?
伝説

2
@Legends:これは静的コンストラクターを持つ非静的クラスにも当てはまりますか?はい。
エリックリペット

24

MSDNによると、静的コンストラクターは次のとおりです。

静的コンストラクターは、最初のインスタンスが作成される前、または静的メンバーが参照される前に、クラスを初期化するために自動的に呼び出されます。

したがって、静的コンストラクターは、静的メソッドMyClass.MyMethod()が呼び出される前に呼び出されます(もちろん、静的構築または静的フィールドの初期化中に呼び出されないと仮定します)。

さて、その中static constructorで非同期で何かをしているのなら、それを同期するのはあなたの仕事です。


7
静的コンストラクターで2番目のスレッドを含む非同期を実行している場合は、苦痛の世界にいます。デッドロックを速くするものはありません。例については、stackoverflow.com / a / 8883117/88656を参照してください。
Eric Lippert 2012

@Eric:同意しました...私はそれをしたくありませんが、MyMethodが呼び出されるまでに彼が何を完成させたいのか正確に彼の例からはわかりませんでした...
James Michael Hare

11

#3は実際には#1です。静的初期化は、それが属するクラスを最初に使用するまで開始されません。

MyMethodが静的コンストラクターまたは静的初期化ブロックから呼び出された場合に可能です。MyMethod静的コンストラクターから直接または間接的に呼び出さない場合は、問題ないはずです。


注意点として、static最適化の適格性に応じて、最初に使用する前に初期化を実際に呼び出すことができることを理解しています。
James Michael Hare


1
静的コンストラクターの場合はtrueですが、静的初期化の場合は私のポイントでした。申し訳ありませんが、「静的初期化が開始されません...」というフレーズでちょっとしたことを選んでいたのかもしれません。これは静的構築には当てはまりますが、クラスに静的コンストラクターがない場合は、静的初期化が以前に発生する可能性があります。
James Michael Hare

申し訳ありませんが、私はおそらく言葉遣いを分析しすぎています。質問の文脈では、それは絶対に正しいです。明示的な静的コンストラクターのないクラスのコンテキストでの静的初期化のスタンドアロンステートメントとしてのその文について心配していました。
James Michael Hare

@ジェームズ:あなたは過度に分析しているわけではありません-ここでは用語が決定的な違いです。静的コンストラクターはC#の概念ですが、型の初期化は.NETのものです。静的コンストラクター(C#)内のコードは、型初期化子(.NET)の一部なりますが、型初期化子がトリガーするタイミング方法(つまり、beforefieldinitセマンティクス)は、C#クラスに静的コンストラクターがあるかどうかによって決まります。
LukeH 2012

9

ドキュメントから(私の強調):

静的コンストラクターは、最初のインスタンスが作成される前、または静的メンバーが参照れる前に、クラスを初期化するために自動的に呼び出されます 。


2

4が常に2の後に来ることを保証できます(静的メソッドからクラスのインスタンスを作成しない場合)が、1と3については同じではありません。


2

静的コンストラクターは、mymethodが実行される前に呼び出されます。ただし、2の前に4が呼び出された場合に失敗した場合は、設計を再考することをお勧めします。とにかく静的コンストラクターで複雑なことをするべきではありません。


2

CLRは、静的メンバーにアクセスする前に静的コンストラクターが実行されることを保証します。しかし、あなたのデザインは少し臭いです。次のようなことを行う方が簡単です。

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

デザインでは、認証が失敗した場合、MyMethodの実行を防ぐ唯一の方法は、例外をスローすることです。


2

メソッドが実行される前に、静的クラスのコンストラクターが呼び出されていることが保証されます。例:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

出力:

Enterキーを押します

// Enterキーを押した後

こんにちは着信..。

こんにちは!

こんにちは!


システムを使用する; 名前空間MyNameSpace {クラスプログラム{静的voidMain(string [] args){Console.WriteLine( "Entered in main"); Boop.SayHi(); Boop.SayHi(); }} static class Boop {static Boop(){Console.Read(); Console.WriteLine( "コンストラクターキーが入力されました"); } public static void SayHi(){Console.WriteLine( "メソッドが呼び出されます"); }}}ええ、このプログラムはより良い理解を与えます
om471987 2012

おそらく。次回は答えとして投稿してください。その場合、より便利で目に見えるようになります。
haiyyu 2012

1

物事が下がる実際の順序は次のとおりです。

  1. 開始 Main
  2. スタティックの開始 MyClassコンストラクターの
  3. 静的の終わり MyClassコンストラクターの
  4. 開始 MyMethod
  5. の終わり Main

0

または、デバッガーでステップスルーすることもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.