Question

Ok, je viens de rencontrer le problème suivant qui a soulevé un sourcil.

Pour diverses raisons, j'ai une configuration de test dans laquelle les classes Testing dans un TestingAssembly.dll dépendent de la classe TestingBase dans un BaseTestingAssembly.dll. Une des choses que la base de test fait dans l’intervalle est la recherche d’une certaine ressource intégrée dans son propre assemblage et l’assembly appelant

Donc, mon BaseTestingAssembly contenait les lignes suivantes ...

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

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

Statiques puisque je pensais que ces assemblages seraient les mêmes pendant toute la durée de vie de l’application, c’est pourquoi il est difficile de les recalculer à chaque test.

Lors de l’exécution de cette opération, j’ai cependant remarqué que _assembly et _calling_assembly étaient définis sur BaseTestingAssembly plutôt que sur BaseTestingAssembly et TestingAssembly, respectivement.

La définition des variables non statiques et leur initialisation dans un constructeur standard ont corrigé le problème, mais je ne comprends pas pourquoi cela est arrivé. Je pensais que les constructeurs statiques étaient exécutés la première fois qu'un membre statique était référencé. Cela ne pouvait provenir que de mon TestingAssembly, qui aurait alors dû être l'appelant. Est-ce que quelqu'un sait ce qui aurait pu se passer?

Était-ce utile?

La solution

Le constructeur statique est appelé par le runtime et non directement par le code utilisateur. Vous pouvez le voir en définissant un point d'arrêt dans le constructeur, puis en l'exécutant dans le débogueur. La fonction située immédiatement au-dessus de celle-ci dans la chaîne d'appels est un code natif.

Modifier : les initialiseurs statiques fonctionnent de différentes manières dans un environnement différent de celui des autres codes utilisateur. Certains autres moyens sont

  1. Ils sont implicitement protégés contre les conditions de concurrence résultant du multithreading
  2. Vous ne pouvez pas intercepter les exceptions en dehors de l'initialiseur

En général, il est probablement préférable de ne pas les utiliser pour des tâches trop sophistiquées. Vous pouvez implémenter single-init avec le modèle suivant:

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

Ajoutez un verrouillage si vous souhaitez un accès multithread.

Autres conseils

Je pense que la réponse est ici dans la discussion sur Constructeurs statiques C # . Ma meilleure hypothèse est que le constructeur statique est appelé à partir d'un contexte inattendu, car:

  

L'utilisateur n'a aucun contrôle sur le moment où le   constructeur statique est exécuté dans le   programme

Assembly.GetCallingAssembly () renvoie simplement l'assembly de la deuxième entrée de la pile d'appels. Cela peut très dépendre de la façon dont votre méthode / getter / constructeur est appelée. Voici ce que j'ai fait dans une bibliothèque pour obtenir l'assemblage de la première méthode qui n'est pas dans ma bibliothèque. (Cela fonctionne même dans les constructeurs statiques.)

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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top