2010-04-16 25 views
35

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?¿Cómo puedo ejecutar un constructor estático?

Pregunta adicional: ¿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 los campos private static readonly, por lo que no se puede ejecutar en un método.

Respuesta

91

Las otras respuestas son excelentes, pero si es necesario forzar un constructor de la clase para funcionar sin tener una referencia al tipo (es decir, la reflexión.), Se puede utilizar:

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

¿Eso ejecuta el _static_ctor? La clase no tiene un constructor público. Además, ¿es seguro que no cause que un ctor se ejecute dos veces, posiblemente? – mafu

+3

Sí, ejecuta el ctor estático. También es seguro, el CLR solo permitirá que el ctor estático se ejecute una vez. –

+1

De hecho, este método se proporciona explícitamente para que los escritores del compilador aseguren la inicialización determinista: http://blogs.msdn.com/cbrumme/archive/2003/04/15/51348.aspx – Ruben

-3

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.

+2

No * * bastante cierto ... Puede funcionar más adelante: http : //msmvps.com/blogs/jon_skeet/archive/2010/01/26/type-initialization-changes-in-net-4-0.aspx –

+7

Estoy constantemente sorprendido por la cantidad de características de lenguaje oscuro que nunca he tenido oído hablar de, probablemente nunca use intencionalmente, pero que probablemente me morderá en el culo porque No estoy al tanto de ellos ... – Ray

+0

Quiero ejecutarlo antes porque lleva mucho tiempo (ver la pregunta editada). – mafu

-1

No es necesario hacer esto, el punto de un static constructor es que se ejecuta 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é que desea hacer esto?

+0

Esto falla en mi caso porque hay campos 'static readonly'. – mafu

+0

Su respuesta es la misma que la de @ Ray, pero la suya la publicó un par de minutos antes. Desafortunadamente, como él, su información también es engañosa. Además del problema mencionado en el comentario anterior (OP), la segunda parte de su propuesta no funcionaría para 'static class MyClass {}' (que había estado disponible desde 2006 en ese momento). –

-1

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.

edición

constructores estáticos se ejecutan cuando any static members are referenced. Simplemente podría crear un método ficticio llamado initialize que no hizo nada más que asegurarse de que el marco llame al constructor estático.

+0

(ver ediciones en OP) – mafu

+0

Consulte los comentarios a la respuesta de Ray sobre el método ficticio. – mafu

16

Solo 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(); 
4

El cctor (constructor estático) será llamado cada vez que se produce uno de los siguientes;

  1. Se crea una instancia de la clase
  2. Cualquier miembro estático se accede
  3. cualquier momento antes de que, si BeforeFieldInit se establece

Si desea invocar explícitamente el cctor, suponiendo que tener otros miembros estáticos, solo invocar/acceder a ellos.

Si no está haciendo nada muy interesante en su cctor, 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

0

¡Los constructores estáticos NO siempre se invocan cuando se accede a un método estático!

Me di cuenta de que si llama 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.

+0

Estoy un poco confundido. Supongamos que tiene dos clases derivadas (con ctors estáticos) y llama a un método estático en la clase base (que también tiene un ctor estático). ¿El ctor estático de qué clase derivada debería llamarse entonces? Dado este razonamiento, el comportamiento que describes no suena extraño. – mafu

+1

@mafu Creo que está hablando de la situación inversa: es cierto que cualquier invocación de un 'static_ctor' (" type initializer ") de alguna clase * derived * no implica de ninguna manera la invocación del tipo initializer de su * base * clase, y esto puede parecer contradictorio a simple vista casual. Sin embargo, saber más sobre el diseño y las capacidades de .NET, CTS y CIL rápidamente disipa la intuición, y es natural entender el comportamiento existente ... principalmente porque es fácil imaginar los casos extremos fuera de control si fueron de otra manera! –

2

La extensión de Fábio de observations, el siguiente programa de prueba completa y expone los detalles JIT sensible de TypeAttributes.BeforeFieldInit comportamiento, comparando .NET 3.5 a la versión más reciente (en los últimos tiempos 2017) .NET 4.7.1, y también demuestra los peligros potenciales para las variaciones del tipo de construcción 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 la salida de la consola de ejecutar este programa en todas las combinaciones de {x86, x64} { y depuración, liberación}. 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á más interesante que la versión 2,0/3,5 frente 4,7 deltas son las diferencias dentro la corriente.versión NET, ya que muestran que, aunque el comportamiento del campo-inicialización hoy en día es más consistente entre x86 y x64 de lo que solía ser, es aún es posible experimentar una diferencia significativa en el comportamiento de inicialización de campo en tiempo de ejecución entre su Debug y Release construye hoy .

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 lo hace presenta un error para su diseño general, es bastante misterioso y difícil de seguir abajo.


Notas
1. El programa anterior utiliza la siguiente función de utilidad para mostrar la versión actual 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 
} 
Cuestiones relacionadas