2008-09-23 15 views
6

Ok, quindi mi sono imbattuto nel seguente problema che ha sollevato un sopracciglio.Assembly.GetCallingAssembly() e costruttori statici?

Per vari motivi, ho una configurazione di test in cui le classi di test in un TestingAssembly.dll dipendono dalla classe TestingBase in un BaseTestingAssembly.dll. Una delle cose che il TestBase fa, nel frattempo, è cercare una certa risorsa incorporata nella propria e l'assemblea chiamata

Quindi il mio BaseTestingAssembly conteneva le seguenti righe ...

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

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

Statico da quando ho capito , questi assemblaggi sarebbero uguali per tutta la durata dell'applicazione, quindi perché preoccuparsi di ricalcolarli su ogni singolo test.

Durante l'esecuzione, tuttavia, ho notato che sia _assembly che _calling_assembly erano impostati su BaseTestingAssembly, rispettivamente su BaseTestingAssembly e TestingAssembly.

L'impostazione delle variabili su non statico e il loro inizializzazione in un costruttore regolare ha risolto questo problema, ma sono confuso sul motivo per cui ciò è accaduto all'inizio. Ho pensato che i costruttori statici funzionassero la prima volta che un membro statico viene referenziato. Questo potrebbe essere stato solo dal mio TestingAssembly che avrebbe dovuto essere il chiamante. Qualcuno sa cosa potrebbe essere successo?

risposta

5

Il costruttore statico è chiamato dal runtime e non direttamente dal codice utente. Puoi vederlo impostando un breakpoint nel costruttore e poi eseguendolo nel debugger. La funzione immediatamente sopra di esso nella catena di chiamate è il codice nativo.

Modifica: Esistono molti modi in cui gli inizializzatori statici vengono eseguiti in un ambiente diverso rispetto ad altri codici utente. Alcuni altri modi sono

  1. Sono implicitamente protetti contro le condizioni di gara che derivi dalla multithreading
  2. Non è possibile intercettare le eccezioni da fuori initializer

In generale, è probabilmente meglio non usarli per qualcosa di troppo sofisticato. È possibile implementare single-init con il seguente modello:

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

Aggiungere il blocco se si prevede l'accesso multithread.

+0

così perché è l'assemblea chiamata non nulla, allora? –

+0

Sarebbe più utile? :) –

+0

No, sembra proprio che avrebbe senso comunque –

1

Penso che la risposta sia qui nella discussione di C# static constructors. La mia ipotesi migliore è che il costruttore statico è sempre chiamato da un contesto inaspettato perché:

L'utente non ha alcun controllo su quando il costruttore statico viene eseguito nel programma

+0

In realtà si ha un certo controllo :) – leppie

1

Assembly.GetCallingAssembly() restituisce semplicemente l'assembly della seconda voce nello stack di chiamate. Ciò può dipendere molto da dove viene chiamato il metodo/getter/costruttore. Ecco cosa ho fatto in una libreria per ottenere l'assemblaggio del primo metodo che non è nella mia libreria. (Funziona anche con costruttori statici.)

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