2008-09-23 16 views
6

Ok, entonces me encontré con el siguiente problema que levantó una ceja.Assembly.GetCallingAssembly() y constructores estáticos?

Por diversas razones, tengo una configuración de prueba donde las clases de prueba en un TestingAssembly.dll dependen de la clase TestingBase en BaseTestingAssembly.dll. Una de las cosas que el TestBase hace mientras tanto es buscar un determinado recurso incrustado en su propia y el conjunto de llamadas

Así que mi BaseTestingAssembly contenía las siguientes líneas ...

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

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

estático ya que pensé , estos ensamblajes serían los mismos a lo largo de la vida útil de la aplicación, entonces, ¿por qué molestarse en volver a calcularlos en cada prueba?

Al ejecutar esto, sin embargo, noté que tanto _assembly como _calling_assembly se estaban configurando en BaseTestingAssembly en lugar de BaseTestingAssembly y TestingAssembly, respectivamente.

Al establecer las variables como no estáticas y tenerlas inicializadas en un constructor normal se corrigió esto, pero no estoy seguro de por qué sucedió esto. Pensé que los constructores estáticos se ejecutan la primera vez que se hace referencia a un miembro estático. Esto solo podría haber sido de mi TestingAssembly, que entonces debería haber sido la persona que llama. ¿Alguien sabe lo que pudo haber pasado?

Respuesta

5

El constructor estático es llamado por el tiempo de ejecución y no directamente por el código de usuario. Puede ver esto estableciendo un punto de interrupción en el constructor y luego ejecutándose en el depurador. La función inmediatamente superior en la cadena de llamadas es código nativo.

Editar: Hay muchas maneras en que los inicializadores estáticos se ejecutan en un entorno diferente al de otros códigos de usuario. Algunas otras maneras son

  1. Están implícitamente protegidos contra las condiciones de carrera que resultan de múltiples hilos
  2. Usted no puede capturar las excepciones desde fuera del inicializador

En general, es mejor no usarlos para cualquier cosa demasiado sofisticada. Puede implementar single-init con el siguiente patrón:

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

Agregue el bloqueo si espera el acceso multiproceso.

+0

¿por qué entonces el conjunto de llamadas no es nulo? –

+0

¿Sería eso más útil? :) –

+0

No, parece que eso tendría sentido –

1

Creo que la respuesta está aquí en la discusión de C# static constructors. Mi mejor conjetura es que el constructor estático se está llamado desde un contexto inesperado porque:

El usuario no tiene control sobre cuando el constructor estático se ejecuta en el programa

+0

En realidad, usted tiene algún control :) – leppie

1

Assembly.GetCallingAssembly() simplemente devuelve el conjunto de la segunda entrada en la pila de llamadas. Eso puede depender mucho de cómo se llame a su método/getter/constructor. Esto es lo que hice en una biblioteca para obtener el ensamblaje del primer método que no está en mi biblioteca. (Esto incluso funciona en constructores 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; 
} 
Cuestiones relacionadas