18

私の質問は、クラスとして宣言される静的メソッドのみを含むクラスをリファクタリングしstatic、アプリケーションの起動時に奇妙な問題が発生した後に発生します。

徹底的な調査は行っていませんが、静的コンストラクター内からの呼び出しが何らかの理由で完了しないようです。

では、C# で静的コンストラクターを使用するときに落とし穴がある場所を知りたいですか? より具体的には、静的コンストラクター内から使用してはならず、絶対に避けるべきものはありますか?

4

3 に答える 3

26

静的コンストラクターにはいくつかの落とし穴があります。たとえば、静的コンストラクターが例外をスローしたTypeInitializationException場合、そのメンバーのいずれかにアクセスするたびに、を取得し続けます。

静的コンストラクターが例外をスローした場合、ランタイムはそれを2回呼び出すことはなく、プログラムが実行されているアプリケーションドメインの存続​​期間中、型は初期化されないままになります。

一般に、静的クラスは、初期化が不要なステートレスシナリオでのみ使用する必要があります。クラスを初期化する必要がある場合は、最初のアクセス時に遅延初期化できるシングルトンパターンを使用する方がよい場合があります。

public class MyClass
{
    private static readonly Lazy<MyClass> current = 
        new Lazy<MyClass>(() => new MyClass());

    public static MyClass Current
    {
        get { return current.Value; }
    }

    private MyClass()
    {
        // Initialization goes here.
    }

    public void Foo()
    {
        // ...
    }

    public void Bar()
    {
        // ...
    }
}

static void Main(string[] args)
{
    MyClass.Current.Foo();   // Initialization only performed here.
    MyClass.Current.Bar();
    MyClass.Current.Foo();
}

編集:この問題についてさらに詳しく調べたところ、静的コンストラクター内でブロッキング操作(非同期コールバックやスレッド同期など)を実行すると、静的コンストラクターによってデッドロックが発生するようです。

CLRは内部的にロックを使用して、型初期化子(静的コンストラクター)が同時に複数回実行されるのを防ぎます。したがって、静的コンストラクターが宣言型の別のメンバーに別のスレッドからアクセスしようとすると、必然的にデッドロックが発生します。「別のメンバー」は、PLINQまたはTPL操作の一部として宣言された無名関数である可能性があるため、これらのバグは微妙で識別が難しい場合があります。

Igor Ostrovsky(MSFT)は、静的コンストラクターのデッドロックに関する記事でこれを説明し、デッドロックの次の例を示しています。

using System.Threading;

class MyClass
{
    static void Main() { /* Won’t run... the static constructor deadlocks */  }

    static MyClass()
    {
        Thread thread = new Thread(arg => { });
        thread.Start();
        thread.Join();
    }
}

{ }上記の例では、新しいスレッドは、コールバックとして定義されている空の無名関数にアクセスする必要があります。ただし、無名関数はMyClassバックグラウンドの別のプライベートメソッドとしてコンパイルされるため、MyClass型が初期化される前に新しいスレッドはそれにアクセスできません。また、MyClass静的コンストラクターは(のために)新しいスレッドが最初に完了するのを待つ必要があるためthread.Join()、デッドロックが発生します。

于 2012-05-05T21:25:02.110 に答える
3

はい、主にクラスの初期化時に関連するいくつかの落とし穴があります。基本的に、静的コンストラクターを持つクラスはbeforefieldinitフラグでマークされないため、ランタイムは後で初期化できます。

詳細については、この記事をご覧ください。

于 2012-05-05T21:04:01.010 に答える
0

これは質問への回答ではありませんが、コメントするには長すぎるので、ここで提供します...

コンストラクトについて知らなかったのでstatic class、次のスキーム (簡略化) を使用してシングルトンを提供しました。

public class SomeSingleton {
    static _instance;
    static public SomeSingleton Instance {
        get {
            if (_instance==null) {
                _instance=new SomeSingleton();
            }
            return _instance;
        }
    }
}

後で、使用します

SomeSingleton.Instance.MyProp = 3;

そして、Instanceメンバーの最初の使用により、シングルトンが構築されます。

そのようなクラスがたくさんある場合、シングルトンのインスタンス化は適切な順序で行われるので、問題はないでしょう。

于 2012-05-05T21:28:35.530 に答える