2010-04-16 11 views
35

Je voudrais exécuter le constructeur statique d'une classe (c'est-à-dire que je veux "charger" la classe) sans créer d'instance. Comment je fais ça? Question de bonus: Y a-t-il des différences entre les versions .NET 4 et les versions plus anciennes?Comment puis-je exécuter un constructeur statique?

Edit:

  • La classe n'est pas statique.
  • Je veux l'exécuter avant de créer des instances car il faut un certain temps pour s'exécuter, et j'aimerais éviter ce délai au premier accès.
  • Le ctor statique initialise les champs private static readonly et ne peut donc pas être exécuté dans une méthode à la place.

Répondre

91

Les autres réponses sont excellents, mais si vous avez besoin de forcer un constructeur de classe à courir sans avoir une référence au type (par exemple de réflexion.), Vous pouvez utiliser:

Type type = ...; 
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle); 
+0

Est-ce que cela fonctionne le _static_ ctor? La classe n'a pas de constructeur public. En outre, est-ce sûr en ce qu'il ne fait pas courir un ctor deux fois, peut-être? – mafu

+3

Oui, il exécute le ctor statique. Il est également sûr, le CLR permettra seulement le ctor statique être exécuté une fois. –

+1

En effet, cette méthode est explicitement prévue pour les auteurs de compilateurs afin d'assurer une initialisation déterministe: http://blogs.msdn.com/cbrumme/archive/2003/04/15/51348.aspx – Ruben

-3

Le constructeur statique s'exécute automatiquement la première fois que vous accédez à la classe. Il n'y a pas besoin (ou capacité) de le "lancer" soi-même.

+2

Pas * tout * vrai ... il peut fonctionner plus tard: http : //msmvps.com/blogs/jon_skeet/archive/2010/01/26/type-initialization-changes-in-net-4-0.aspx –

+7

Je suis constamment étonné par le nombre de fonctionnalités de langage obscures que je n'ai jamais entendu parler, ne sera probablement jamais utilisé intentionnellement, mais qui me mordra probablement dans le cul parce que Je ne suis pas au courant d'eux ... – Ray

+0

Je veux l'exécuter plus tôt car cela prend beaucoup de temps (voir question éditée). – mafu

-1

Il n'est pas nécessaire de le faire, le point entier d'un static constructor est qu'il s'exécute une fois lors de la première initialisation de la classe au premier accès. Si vous voulez exécuter quelque chose à la demande, pensez à ajouter votre code d'initialisation dans une méthode publique appelée par le constructeur. Vous pouvez ensuite appeler cette méthode quand vous le souhaitez. Mais je ne suis pas sûr pourquoi vous voudriez faire ceci?

+0

Cela échoue dans mon cas car il existe des champs 'static readonly'. – mafu

+0

Votre réponse est la même que celle de @ Ray, mais elle est postée quelques minutes plus tôt. Malheureusement, comme le sien, votre information est aussi trompeuse. Outre le problème mentionné dans le commentaire précédent (PO), la deuxième partie de votre proposition ne fonctionnerait pas pour 'static class MyClass {}' (qui était disponible depuis 2006 à ce moment-là). –

-1

Comme d'autres l'ont dit, les constructeurs statiques s'exécutent automatiquement. Si vous avez besoin d'être explicite, peut-être devriez-vous le refactoriser en une méthode statique que vous pouvez exécuter explicitement?

L'appel explicite d'une méthode statique assurerait, bien sûr, que le constructeur statique ait été exécuté.

modifier

constructeurs statiques sont exécutés lorsque any static members are referenced. Vous pouvez simplement créer une méthode factice appelée initialize qui n'a fait que s'assurer que le framework appelle le constructeur statique.

+0

(voir modifications dans OP) – mafu

+0

Voir les commentaires sur la réponse de Ray à propos de la méthode dummy. – mafu

16

Référencez simplement l'un de vos champs statiques. Cela forcera votre code d'initialisation statique à s'exécuter. Par exemple:

public class MyClass 
{ 
    private static readonly int someStaticField; 

    static MyClass() { someStaticField = 1; } 

    // any no-op method call accepting your object will do fine 
    public static void TouchMe() { GC.KeepAlive(someStaticField); } 
} 

Utilisation:

// initialize statics 
MyClass.TouchMe(); 
4

Le cctor (constructeur statique) sera appelé à chaque fois que l'un des cas suivants;

  1. Vous créez une instance de la classe
  2. Tout membre statique est accessible
  3. En tout temps avant que, si BeforeFieldInit est réglé

Si vous voulez invoquer explicitement la cctor, en supposant avoir d'autres membres statiques, juste les invoquer/y accéder.

Si vous ne faites rien de très intéressant dans votre cctor, le compilateur peut décider de le marquer BeforeFieldInit, ce qui permettra au CLR d'exécuter le cctor plus tôt.Ceci est expliqué plus en détail ici: http://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx

0

Les constructeurs statiques ne sont PAS toujours appelés lors de l'accès à une méthode statique!

J'ai remarqué que si vous appelez une méthode statique dans une classe de base, le constructeur statique de la super classe n'est pas appelé. Ce comportement inattendu a mordu plusieurs fois.

+0

Je suis un peu confus. Supposons que vous ayez deux classes dérivées (avec des cteurs statiques) et que vous appeliez une méthode statique dans la classe de base (qui a aussi un ctor statique). Le ctor statique de quelle classe dérivée devrait s'appeler alors? Compte tenu de ce raisonnement, le comportement que vous décrivez ne semble pas étrange. – mafu

+1

@mafu Je pense qu'il parle de la situation inverse: il est vrai que toute invocation d'un 'static _ctor' (" type initializer ") d'une classe * derived * n'entraine en aucun cas l'invocation du type initializer de sa * base * classe, et cela peut sembler contre-intuitif au premier coup d'œil. En savoir plus sur la conception et les capacités .NET, CTS et CIL dissipe rapidement l'intuition, et il devient naturel de comprendre le comportement existant ... principalement parce qu'il devient facile d'imaginer les cas de bord hors de contrôle si elle étaient autrement! –

2

Extension du programme de test et complet ci-dessous de observations, Fábio expose les détails JIT sensibles du comportement TypeAttributes.BeforeFieldInit, en comparant .NET 3.5 à la dernière version (à partir de fin 2017) .NET 4.7.1 et démontre également les dangers potentiels pour les variations de type de construction dans chaque version elle-même. [1]

using System; 
using System.Diagnostics; 

class MyClass 
{ 
    public static Object _field = Program.init(); 

    public static void TouchMe() { } 
}; 

class Program 
{ 
    static String methodcall, fieldinit; 

    public static Object init() { return fieldinit = "fieldinit"; } 

    static void Main(String[] args) 
    { 
     if (args.Length != 0) 
     { 
      methodcall = "TouchMe"; 
      MyClass.TouchMe(); 
     } 
     Console.WriteLine("{0,18} {1,7} {2}", clrver(), methodcall, fieldinit); 
    } 
}; 

est inférieure à la sortie de la console de l'exécution de ce programme dans toutes les combinaisons de {x86, x64} et {Debug, Release}. J'ai ajouté manuellement un symbole delta Δ (non émis par le programme) pour mettre en évidence les différences entre les deux versions .NET.

2,0 .NET/3,5

2.0.50727.8825 x86 Debug
2.0.50727.8825 x86 Debug TouchMe fieldinit
2.0.50727.8825 x86 Release fieldinit
2.0.50727.8825 x86 Release TouchMe fieldinit
2.0.50727.8825 x64 Debug
2.0.50727.8825 x64 Debug TouchMe fieldinit
2.0.50727.8825 x64 Release
2.0.50727.8825 x64 Release TouchMe fieldinit

.NET 4.7.1

4.7.2556.0 x86 Debug
4.7.2556.0 x86 Debug TouchMe fieldinit
4.7.2556.0 x86 Release Δ
4.7.2556.0 x86 Release TouchMe Δ
4.7.2556.0 x64 Debug
4.7.2556.0 x64 Debug TouchMe fieldinit
4.7.2556.0 x64 Release
4.7.2556.0 x64 Release TouchMe Δ

Comme indiqué dans l'introduction, peut-être plus intéressant que la version 2,0/3,5 par rapport 4.7 deltas sont les différences dans le courant.Version NET, car ils montrent que, bien que le comportement d'initialisation de champ est aujourd'hui plus cohérent entre x86 et x64 qu'auparavant, il est encore encore possible de connaître une différence significative dans le comportement d'initialisation du champ d'exécution entre vos Debug et Release construit aujourd'hui . La sémantique dépend si vous appelez ou non une méthode statique disjointe ou apparemment sans relation sur la classe, donc si cela introduit un bug pour votre conception globale, il est susceptible d'être assez mystérieux et difficile à suivre vers le bas.


Remarques
1. Le programme ci-dessus utilise la fonction d'utilité suivante pour afficher la version actuelle CLR:

static String clrver() 
{ 
    var s = typeof(Uri).Assembly.Location; 
    return FileVersionInfo.GetVersionInfo(s).ProductVersion.PadRight(14) + 
     (IntPtr.Size == 4 ? " x86 " : " x64 ") + 
#if DEBUG 
     "Debug "; 
#else 
     "Release"; 
#endif 
}