Assembly.GetCallingAssembly () и статические конструкторы? - PullRequest
6 голосов
/ 23 сентября 2008

Хорошо, я столкнулся со следующей проблемой, которая подняла бровь.

По разным причинам у меня есть настройка тестирования, в которой классы тестирования в 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 ]

5 голосов
/ 23 сентября 2008

Статический конструктор вызывается средой выполнения, а не напрямую кодом пользователя. Это можно увидеть, установив точку останова в конструкторе, а затем запустив его в отладчике. Функция непосредственно над ней в цепочке вызовов является собственным кодом.

Редактировать: Существует множество способов запуска статических инициализаторов в другой среде, отличной от другого пользовательского кода. Некоторые другие способы

  1. Они неявно защищены от состояний гонки в результате многопоточности
  2. Вы не можете перехватить исключения извне инициализатора

В общем, лучше не использовать их для чего-то слишком сложного. Вы можете реализовать 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;
  }
}

Добавить блокировку, если вы ожидаете многопоточный доступ.

1 голос
/ 04 декабря 2008

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;
}
1 голос
/ 23 сентября 2008

Я думаю, что ответ здесь в обсуждении C # статических конструкторов . Я думаю, что статический конструктор вызывается из неожиданного контекста, потому что:

Пользователь не может контролировать, когда Статический конструктор выполняется в Программа

...