2008-09-23 2 views
6

Итак, я просто столкнулся с следующей проблемой, которая подняла бровь.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(); 
    } 
} 

Static, так как я понял , эти сборки будут одинаковыми в течение срока действия приложения, поэтому зачем беспокоиться о пересчете их на каждом отдельном тесте.

При выполнении этого, однако, я заметил, что как _assembly, так и _calling_assembly устанавливаются в BaseTestingAssembly, а не BaseTestingAssembly и TestingAssembly соответственно.

Установка переменных в нестатические и их инициализация в регулярном конструкторе исправила это, но я смущен, почему это произошло, чтобы начать это. Я думал, что статические конструкторы запускаются в первый раз, когда статический член получает ссылку. Это может быть только из моей TestingAssembly, которая должна была быть вызывающей. Кто-нибудь знает, что могло бы произойти?

ответ

5

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

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

  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; 
    } 
} 

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

+0

так почему же вызывающая сборка не равна нулю? –

+0

Это было бы более полезно? :) –

+0

Нет, похоже, что это имело бы смысл, хотя –

1

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

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

+0

На самом деле у вас есть контроль :) – leppie

1

Assembly.GetCallingAssembly() просто возвращает сборку второй записи в стеке вызовов. Это может сильно зависеть от того, как вызывается метод/getter/constructor. Вот что я сделал в библиотеке, чтобы получить сборку первого метода, которого нет в моей библиотеке. (Это даже работает в статических конструкторах.)

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; 
} 
Смежные вопросы