Assembly.GetCallingAssembly() и статические конструкторы?
Хорошо, я столкнулся со следующей проблемой, которая подняла бровь.
По разным причинам у меня есть настройка тестирования, при которой классы тестирования в TestingAssembly.dll зависят от класса TestingBase в BaseTestingAssembly.dll. В то же время TestBase ищет определенный встроенный ресурс самостоятельно и вызывающую сборку.
Итак, моя 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, а не BaseTestingAssembly и TestingAssembly соответственно.
Установка переменных на нестатические и инициализация их в обычном конструкторе исправила это, но я запутался, почему это случилось, чтобы начать это. Я думал, что статические конструкторы запускаются при первом обращении к статическому члену. Это могло быть только из моей TestingAssembly, которая должна была быть вызывающей. Кто-нибудь знает, что могло случиться?
3 ответа
Статический конструктор вызывается средой выполнения, а не напрямую кодом пользователя. Это можно увидеть, установив точку останова в конструкторе, а затем запустив ее в отладчике. Функция непосредственно над ней в цепочке вызовов - это собственный код.
Изменить: Есть много способов, в которых статические инициализаторы работают в другой среде, чем другой пользовательский код. Некоторые другие способы
- Они неявно защищены от состояния гонки в результате многопоточности
- Вы не можете поймать исключения извне инициализатора
В общем, вероятно, лучше не использовать их для чего-то слишком сложного. Вы можете реализовать single-init по следующей схеме:
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() просто возвращает сборку второй записи в стеке вызовов. Это может очень зависеть от того, как вызывается ваш метод / метод получения / конструктор. Вот что я сделал в библиотеке, чтобы получить сборку первого метода, которого нет в моей библиотеке. (Это даже работает в статических конструкторах.)
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;
}