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:
private static readonly
ne peut donc pas être exécuté dans une méthode.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);
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();
Le cctor (constructeur statique) sera appelé chaque fois que l’une des situations suivantes se produit:
BeforeFieldInit
est définiSi 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
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
}
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.
Vous pouvez aussi faire ceci: Type.TypeInitializer.Invoke (null, null);