Domanda

Ok, quindi ho appena incontrato il seguente problema che ha sollevato un sopracciglio.

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 TestBase fa nel frattempo è cercare una certa risorsa incorporata nel proprio e nell'assembly chiamante

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 pensato, questi assemblaggi sarebbero stati gli stessi 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 venivano impostati su BaseTestingAssembly anziché su BaseTestingAssembly e TestingAssembly rispettivamente.

L'impostazione delle variabili su non statica e la loro inizializzazione in un costruttore normale hanno risolto questo problema, ma sono confuso sul perché sia ??successo per iniziare. Pensavo che i costruttori statici funzionassero la prima volta che si fa riferimento a un membro statico. Questo avrebbe potuto essere solo dal mio TestingAssembly che avrebbe dovuto essere il chiamante. Qualcuno sa cosa potrebbe essere successo?

È stato utile?

Soluzione

Il costruttore statico viene chiamato dal runtime e non direttamente dal codice utente. Puoi vederlo impostando un breakpoint nel costruttore e quindi eseguendolo nel debugger. La funzione immediatamente sopra di essa 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 dalle condizioni di gara risultanti dal multithreading
  2. Non è possibile rilevare eccezioni dall'esterno dell'inizializzatore

In generale, probabilmente è meglio non usarli per qualcosa di troppo sofisticato. Puoi 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;
  }
}

Aggiungi blocco se prevedi l'accesso multithread.

Altri suggerimenti

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

  

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

Assembly.GetCallingAssembly () restituisce semplicemente l'assembly della seconda voce nello stack di chiamate. Ciò può dipendere molto da come 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top