web-dev-qa-db-fra.com

Comment puis-je exécuter un constructeur statique?

J'aimerais 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 bonus: existe-t-il des différences entre .NET 4 et les versions antérieures?

Modifier:

  • La classe n'est pas statique.
  • Je veux l'exécuter avant de créer des instances car son exécution prend un certain temps et j'aimerais éviter ce délai lors du premier accès.
  • Le compteur statique initialise les champs private static readonly ne peut donc pas être exécuté dans une méthode.
38
mafu

Les autres réponses sont excellentes, mais si vous devez forcer un constructeur de classe à s'exécuter sans avoir de référence au type (c'est-à-dire une réflexion), vous pouvez utiliser:

Type type = ...;
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
97
Gary Linscott

Il suffit de référencer 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);
}

Usage:

// initialize statics
MyClass.TouchMe();
16
Fábio Batista

Le cctor (constructeur statique) sera appelé chaque fois que l’une des situations suivantes se produit:

  1. Vous créez une instance de la classe
  2. Tout membre statique est accédé
  3. À tout moment avant cela, si BeforeFieldInit est défini

Si vous voulez appeler explicitement le cctor, en supposant que vous avez d'autres membres statiques, appelez-les/accédez-y.

Si vous ne faites rien de très intéressant dans votre cctor, le compilateur peut décider de le marquer BeforeFieldInit, ce qui donnera au CLR l’option 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

4
csauve

En prolongeant le observations de Fábio, le programme de test court et complet suivant expose les détails du comportement TypeAttributes.BeforeFieldInit relatifs à JIT, en comparant .NET 3.5à la dernière version (fin 2017) . NET 4.7.1, et illustre également les dangers potentiels liés aux 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);
    }
};

Ci-dessous se trouve la sortie de la console résultant 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 surbrillance le symbole. différences entre les deux versions .NET.

_ (.NET 2.0/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'intro, peut-être plus intéressant que la version 2.0/ 3.5versus 4.7les deltas sont les différences dans) la version .NET actuelle, car elles montrent que Bien que le comportement d'initialisation de champ soit aujourd'hui plus cohérent entre x86 et x64 qu'auparavant, il est toujours possible de il est toujours possible de constater une différence significative dans le comportement d'initialisation de champ d'exécution} entre vos versions Debug et Release aujourd'hui.

La sémantique dépendra du fait que vous appeliez une méthode statique disjointe ou apparemment non apparentée sur la classe. Par conséquent, si cela introduit un bogue pour votre conception globale, il sera probablement assez mystérieux et difficile à localiser.


Notes
1. Le programme ci-dessus utilise la fonction utilitaire suivante pour afficher le CLRversion) actuel:

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
}
3
Glenn Slayden

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
ScottK

Vous pouvez aussi faire ceci: Type.TypeInitializer.Invoke (null, null);

0
zumalifeguard