私の質問は、クラスとして宣言される静的メソッドのみを含むクラスをリファクタリングしstatic
、アプリケーションの起動時に奇妙な問題が発生した後に発生します。
徹底的な調査は行っていませんが、静的コンストラクター内からの呼び出しが何らかの理由で完了しないようです。
では、C# で静的コンストラクターを使用するときに落とし穴がある場所を知りたいですか? より具体的には、静的コンストラクター内から使用してはならず、絶対に避けるべきものはありますか?
静的コンストラクターにはいくつかの落とし穴があります。たとえば、静的コンストラクターが例外をスローした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()
、デッドロックが発生します。
はい、主にクラスの初期化時に関連するいくつかの落とし穴があります。基本的に、静的コンストラクターを持つクラスはbeforefieldinit
フラグでマークされないため、ランタイムは後で初期化できます。
詳細については、この記事をご覧ください。
これは質問への回答ではありませんが、コメントするには長すぎるので、ここで提供します...
コンストラクトについて知らなかったので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
メンバーの最初の使用により、シングルトンが構築されます。
そのようなクラスがたくさんある場合、シングルトンのインスタンス化は適切な順序で行われるので、問題はないでしょう。