Assembly.GetCallingAssembly() と静的コンストラクター?
-
02-07-2019 - |
質問
さて、次のような眉をひそめるような問題に遭遇しました。
さまざまな理由から、TestingAssembly.dll の Testing クラスが BaseTestingAssembly.dll の TestingBase クラスに依存するテスト設定があります。その間に TestBase が行うことの 1 つは、それ自体と呼び出しアセンブリ内の特定の埋め込みリソースを探すことです。
したがって、私のBaseTestingAssemblyには次の行が含まれていました...
public class TestBase {
private static Assembly _assembly;
private static Assembly _calling_assembly;
static TestBase() {
_assembly = Assembly.GetExecutingAssembly();
_calling_assembly = Assembly.GetCallingAssembly();
}
}
これらのアセンブリはアプリケーションの存続期間中同じであると考えたので、静的であるため、テストのたびにわざわざ再計算する必要はありません。
ただし、これを実行すると、_assembly と _calling_assembly の両方が、それぞれ BaseTestingAssembly と TestingAssembly ではなく、BaseTestingAssembly に設定されていることに気付きました。
変数を非静的に設定し、通常のコンストラクターで初期化するとこの問題は修正されましたが、なぜこれが始まったのか混乱しています。静的コンストラクターは、静的メンバーが初めて参照されるときに実行されると思っていました。これは、呼び出し元であるはずの私の TestingAssembly からのみ発生した可能性があります。何が起こったのか知っている人はいますか?
解決
静的コンストラクターは、ユーザー コードによって直接呼び出されるのではなく、ランタイムによって呼び出されます。これは、コンストラクターでブレークポイントを設定し、デバッガーで実行すると確認できます。呼び出しチェーン内でそのすぐ上の関数はネイティブ コードです。
編集: 静的イニシャライザを他のユーザー コードとは異なる環境で実行する方法は数多くあります。他のいくつかの方法は、
- マルチスレッドによって生じる競合状態から暗黙的に保護されています。
- イニシャライザの外部から例外をキャッチすることはできません
一般に、あまり洗練されたものには使用しないほうがよいでしょう。次のパターンで単一初期化を実装できます。
private static Assembly _assembly;
private static Assembly Assembly {
get {
if (_assembly == null) _assembly = Assembly.GetExecutingAssembly();
return _assembly;
}
}
private static Assembly _calling_assembly;
private static Assembly CallingAssembly {
get {
if (_calling_assembly == null) _calling_assembly = Assembly.GetCallingAssembly();
return _calling_assembly;
}
}
マルチスレッドアクセスが予想される場合は、ロックを追加します。
他のヒント
答えはここでの議論の中にあると思います C# 静的コンストラクター. 。私の推測では、次の理由により、静的コンストラクターが予期しないコンテキストから呼び出されていると考えられます。
ユーザーは、プログラムで静的コンストラクターがいつ実行されるかを制御できません
Assembly.GetCallingAssembly() は、コール スタック内の 2 番目のエントリのアセンブリを返すだけです。それは、メソッド/ゲッター/コンストラクターがどこでどのように呼び出されるかに大きく依存します。これは、ライブラリにない最初のメソッドのアセンブリを取得するためにライブラリで行ったことです。(これは静的コンストラクターでも機能します。)
private static Assembly GetMyCallingAssembly()
{
Assembly me = Assembly.GetExecutingAssembly();
StackTrace st = new StackTrace(false);
foreach (StackFrame frame in st.GetFrames())
{
MethodBase m = frame.GetMethod();
if (m != null && m.DeclaringType != null && m.DeclaringType.Assembly != me)
return m.DeclaringType.Assembly;
}
return null;
}