predeterminado - llamar una clase en c#
¿Cómo puedo ejecutar un constructor estático? (9)
Me gustaría ejecutar el constructor estático de una clase (es decir, quiero "cargar" la clase) sin crear una instancia. ¿Cómo puedo hacer eso?
Pregunta de bonificación: ¿Hay alguna diferencia entre .NET 4 y versiones anteriores?
Editar:
- La clase no es estática.
- Quiero ejecutarlo antes de crear instancias porque lleva un tiempo ejecutarlo, y me gustaría evitar este retraso en el primer acceso.
- El controlador estático inicializa
private static readonly
camposprivate static readonly
, por lo que no se puede ejecutar en un método.
Como han dicho otros, los constructores estáticos se ejecutan automáticamente. Si necesita ser explícito, ¿quizás debería refactorizarlo en un método estático que pueda ejecutar explícitamente?
Llamar explícitamente a un método estático también garantizaría, por supuesto, que se haya ejecutado el constructor estático.
editar
Los constructores estáticos se ejecutan cuando se hace referencia a cualquier miembro estático . Simplemente podría crear un método ficticio llamado initialize
que no hizo nada más que asegurarse de que el framework llama al constructor estático.
El constructor estático se ejecuta automáticamente la primera vez que accede a la clase. No hay necesidad (o capacidad) de ejecutarlo usted mismo.
Extendiendo las observations de Fábio, el siguiente programa de prueba corto y completo expone los detalles sensibles al JIT del comportamiento de BeforeFieldInit , comparando .NET 3.5 con la última versión (a fines de 2017) .NET 4.7.1 , y también demuestra los peligros potenciales para construir variaciones de tipo dentro de cada versión. [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);
}
};
A continuación se muestra el resultado de la consola al ejecutar este programa en todas las combinaciones de {x86, x64} y {Debug, Release} . Agregué manualmente un símbolo delta Δ
(no emitido por el programa) para resaltar las diferencias entre las dos versiones de .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 Δ
Como se señaló en la introducción, quizás más interesantes que la versión 2.0 / 3.5 versus 4.7 deltas son las diferencias dentro de la versión actual de .NET, ya que muestran que, aunque el comportamiento de inicialización de campo en la actualidad es más consistente entre x86
y x64
de lo que usaba De todos modos , todavía es posible experimentar una diferencia significativa en el comportamiento de inicialización del campo en tiempo de ejecución entre sus Release
Debug
y Release
.
La semántica dependerá de si llama a un método estático disjunto o aparentemente no relacionado en la clase o no, por lo que si se presenta un error para su diseño general, es probable que sea bastante misterioso y difícil de rastrear.
Notas
1. El programa anterior usa la siguiente función de utilidad para mostrar la versión actual de 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
}
Las otras respuestas son excelentes, pero si necesita obligar a un constructor de clase a ejecutar sin tener una referencia al tipo (es decir, reflexión), puede usar:
Type type = ...;
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
Los constructores estáticos NO siempre se llaman cuando se accede a un método estático.
Noté que si llamas a un método estático en una clase base, NO se llama al constructor estático de la superclase. Este comportamiento inesperado ha mordido muchas veces.
No es necesario hacer esto, el objetivo de un constructor estático es que se ejecute una vez cuando la clase se inicializa por primera vez en el primer acceso. Si desea ejecutar algo a pedido, considere agregar su código de inicialización en un método público que el constructor llama. A continuación, puede llamar a este método cuando lo desee. Pero no estoy seguro de por qué querrías hacer esto?
Se llamará al cctor (constructor estático) siempre que ocurra una de las siguientes situaciones;
- Usted crea una instancia de la clase
- Se accede a cualquier miembro estático
- En cualquier momento antes de eso, si se establece
BeforeFieldInit
Si desea invocar explícitamente el cctor, suponiendo que tiene otros miembros estáticos, simplemente invoque / acceda a ellos.
Si no está haciendo nada muy interesante en su directorio, el compilador puede decidir marcarlo BeforeFieldInit
, lo que le permitirá al CLR la opción de ejecutar el cctor anticipadamente. Esto se explica con más detalle aquí: http://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx
Simplemente haga referencia a uno de sus campos estáticos. Esto forzará la ejecución de su código de inicialización estático. Por ejemplo:
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);
}
Uso:
// initialize statics
MyClass.TouchMe();
También puede hacer esto: type.TypeInitializer.Invoke (null, null);