Pergunta

Ok, então eu corri para o seguinte problema que levantou uma sobrancelha.

Por várias razões Eu tenho uma configuração de teste onde as aulas testes em um TestingAssembly.dll dependem da classe TestingBase em um BaseTestingAssembly.dll. Uma das coisas que o TestBase faz, entretanto é olhar para um determinado recurso incorporado em seu próprio eo chamando de montagem

Assim, a minha BaseTestingAssembly continha as seguintes linhas ...

public class TestBase {    
  private static Assembly _assembly;
  private static Assembly _calling_assembly;

  static TestBase() {
    _assembly = Assembly.GetExecutingAssembly();
    _calling_assembly = Assembly.GetCallingAssembly();
  }
}

estática desde que eu descobri, estes conjuntos seria o mesmo durante a vida útil do aplicativo então porque se preocupar recalcular-los em todos os testes.

Ao executar este no entanto eu notei que tanto _assembly e _calling_assembly estavam sendo definido como BaseTestingAssembly em vez de BaseTestingAssembly e TestingAssembly respectivamente.

A definição de variáveis ??de não-estático e tê-los inicializado em um construtor fixo regular isso, mas eu estou confuso por que isso aconteceu para começar este. Pensei construtores estáticos executar a primeira vez que um membro estático fica referenciado. Isso só poderia ter sido da minha TestingAssembly, que deve ter sido o autor da chamada. Alguém sabe o que poderia ter acontecido?

Foi útil?

Solução

O construtor estático é chamado pelo tempo de execução e não diretamente pelo código do usuário. Você pode ver isso definindo um ponto de interrupção no construtor e, em seguida, em execução no depurador. A função imediatamente acima dele na cadeia de chamada é código nativo.

Editar: Há uma série de maneiras em que inicializadores estáticos são executados em um ambiente diferente do que o outro código de utilizador. Algumas outras maneiras são

  1. Eles estão protegidos implicitamente contra as condições de corrida resultantes de multithreading
  2. Você não pode capturar exceções de fora da initializer

Em geral, provavelmente é melhor não usá-los para nada muito sofisticado. Você pode implementar single-inicialização com o seguinte padrão:

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

Adicionar bloqueio se você espera que o acesso de vários segmentos.

Outras dicas

Eu acho que a resposta está aqui na discussão dos C construtores # estáticos . Meu melhor palpite é que o construtor estático está sendo chamado de um contexto inesperado porque:

O usuário não tem controle sobre quando o construtor estático é executado no programa

Assembly.GetCallingAssembly () simplesmente retorna a montagem da segunda entrada na pilha de chamadas. Isso pode muito dependendo de onde como seu método / getter / construtor é chamado. Aqui está o que eu fiz em uma biblioteca para obter a montagem do primeiro método que não está na minha biblioteca. (Isso funciona mesmo em construtores estáticos).

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top