Assembly.GetCallingAssembly()和静态构造函数?

时间:2020-03-06 14:36:28  来源:igfitidea点击:

好的,所以我遇到了以下引起人们关注的问题。

由于各种原因,我有一个测试设置,其中TestingAssembly.dll中的Testing类依赖于BaseTestingAssembly.dll中的TestingBase类。
同时,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,然后应该是调用方。有人知道会发生什么吗?

解决方案

我认为答案就在Cstatic构造函数的讨论中。我最好的猜测是,静态构造函数是从意外的上下文中调用的,因为:

The user has no control on when the
  static constructor is executed in the
  program

静态构造函数由运行时调用,而不是由用户代码直接调用。我们可以通过在构造函数中设置断点,然后在调试器中运行来查看此情况。调用链中紧接其上的函数是本机代码。

编辑:有很多方法可以使静态初始化程序在不同于其他用户代码的环境中运行。其他一些方法是

  • 它们受到隐式保护,以防止多线程导致的竞争条件
  • 我们无法从初始化程序外部捕获异常

通常,最好不要将它们用于任何过于复杂的事情。我们可以使用以下模式实现单初始化:

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

如果我们期望多线程访问,请添加锁定。

Assembly.GetCallingAssembly()仅返回调用堆栈中第二个条目的程序集。这很大程度上取决于方法/ getter /构造函数的调用位置。这是我在库中所做的操作,以获取库中未包含的第一个方法的程序集。 (这甚至可以在静态构造函数中使用。)

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