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
}
¿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
Sí, ejecuta el ctor estático. También es seguro, el CLR solo permitirá que el ctor estático se ejecute una vez. –
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