Pregunta

Tengo una pregunta acerca de los constructores de tipos dentro de un tipo de Valor.Esta pregunta fue inspirado por algo que Jeffrey Richter escribió en CLR a través de C# 3a ed, dice (en la página 195 - capítulo 8) que nunca se debe definir un constructor de tipo dentro de un tipo de valor, ya que hay veces cuando el CLR no la llaman.

Así, por ejemplo (bueno...Jeffrey Richters ejemplo, en realidad), no puedo trabajar, incluso mirando la IL, ¿por qué el constructor de tipo no se llama en el siguiente código:

internal struct SomeValType
{
    static SomeValType()
    {
        Console.WriteLine("This never gets displayed");
    }
    public Int32 _x;
}
public sealed class Program
{
    static void Main(string[] args)
    {
        SomeValType[] a = new SomeValType[10];
        a[0]._x = 123;
        Console.WriteLine(a[0]._x);     //Displays 123
    }
}

Así, la aplicación de las siguientes reglas para los constructores de tipos no puedo ver por qué el tipo de valor constructor de arriba no es llamado a todos.

  1. Podemos definir un valor estático tipo de constructor para establecer el estado inicial del tipo.
  2. Un tipo no puede tener más de un constructor - no hay ningún defecto.
  3. Los constructores de tipos son implícitamente privado
  4. El compilador JIT, se comprueba si el tipo del tipo de constructor ya se ha ejecutado en este dominio de aplicación.Si no se emite la llamada en código nativo, si no, no como se sabe el tipo ya está 'inicializa'.

Así que...simplemente no puedo averiguar por qué yo no puedo ver este tipo de matriz que se está construyendo.

Mi mejor conjetura sería que podría ser:

  1. La forma en que el CLR construye un tipo de matriz.Yo habría pensado que el constructor estático sería llamado cuando el primer elemento fue creado
  2. El código en el constructor no está inicializando cualquiera de los campos estáticos por lo que se omite.He experimentado con la inicialización de campos estáticos privados dentro del constructor, pero el campo sigue siendo el valor predeterminado 0 valor - por lo tanto, el constructor no es llamado.
  3. O...el compilador es de alguna manera la optimización de la distancia de la llamada al constructor debe a que el público Int32 se establece -, sino que es una difusa adivinar mejor!!

Mejores prácticas, etc lado, estoy super intrigada por como quiero ser capaz de ver por mí mismo por qué no se llama.

EDITAR:He añadido una respuesta a mi propia pregunta, sólo una cita de lo que Jeffrey Richter dice acerca de ella.

Si alguien tiene alguna idea entonces sería brillante.Muchas gracias, James

¿Fue útil?

Solución

the Microsoft C # 4 Spec ha cambiado ligeramente de versiones anteriores y ahora refleja con mayor precisión el comportamiento que estamos viendo aquí:

11.3.10 constructores estáticos

constructores estáticos para estructuras siguen La mayoría de las mismas reglas que para las clases. La ejecución de un constructor estático. Para un tipo de estructura está activado por el Primero de los siguientes eventos a ocurrir. Dentro de un dominio de aplicación:

  • se hace referencia a un miembro estático del tipo de estructura.
  • Se llama un constructor declarado explícitamente del tipo de estructura.

La creación de valores predeterminados (& # 167; 11.3.4) de los tipos de estructuras no lo hace desencadenar al constructor estático. (Un Ejemplo de este es el valor inicial. de elementos en una matriz.)

The especificaciones de ECMA y el Microsoft C # 3 Spec Ambos tienen un evento adicional en esa lista: "Se hace referencia a un miembro de la instancia del tipo de estructura". , por lo que parece que C. # 3 estuviera en contravención de su propia especificación aquí. La especificación C # 4 se ha puesto en alineación más estrecha con el comportamiento real de C # 3 y 4.

editar ...

Después de una investigación adicional, parece que casi todo el acceso de los miembros de la instancia, excepto , el acceso directo del campo se activará el constructor estático (al menos en las implementaciones actuales de Microsoft de C # 3 y 4).

Por lo tanto, las implementaciones actuales están más estrechamente relacionadas con las reglas dadas en las especificaciones de ECMA y C # 3 que las de la especificación C # 4: las reglas C # 3 se implementan correctamente al acceder a todos los miembros de la instancia , excepto < / em> campos; Las reglas C # 4 son solo implementadas correctamente para el acceso de campo.

(Las diferentes especificaciones están todos de acuerdo, y aparentemente se implementan correctamente, cuando se trata de las reglas relacionadas con el acceso de los miembros estáticos y los constructores declarados explícitamente).

Otros consejos

§18.3.10 de la norma (véase también El lenguaje de programación C# libro):

La ejecución de un constructor estático de una estructura que se desencadena por el primero de los siguientes eventos que se producen dentro de un dominio de aplicación:

  • Un miembro de instancia de la estructura es se hace referencia.
  • Un miembro estático de la estructura se hace referencia.
  • Un explícitamente declarado constructor de la struct se llama.

[Nota:La creación de los valores por defecto (§18.3.4) de la estructura tipos de no desencadenar la estática constructor.(Un ejemplo de esto es el valor inicial de los elementos en una de la matriz.) nota final]

Así que estoy de acuerdo con usted en que las dos últimas líneas de su programa debe desencadenar la primera regla.

Después de las pruebas, el consenso parece ser que constantemente desencadenantes de métodos, propiedades, eventos, y los indizadores.Eso significa que es correcto para todos explícito de los miembros de instancia excepto campos.Así que si Microsoft C# 4 las normas fueron elegidos para el estándar, que hacen de su implementación, van en su mayoría a la derecha, principalmente, al mal.

Solo poniendo esto como una "respuesta" para poder compartir lo que escribió el mismo Sr. Richter (¿Alguien tiene un enlace para la última especificación de CLR por cierto, es fácil obtener la edición de 2006 pero encontrarla una Un poco más difícil para obtener el último):

Para este tipo de cosas, generalmente es mejor mirar la especificación CLR que la especificación C #. La especificación CLR dice:

4. Si no está marcado antes de que se ejecute el método del inicializador de ese tipo en (es decir, se activa):

• Primer acceso a cualquier campo estático de ese tipo, o

• Primera invocación de cualquier método estático de ese tipo o

• Primera invocación de cualquier instancia o método virtual de ese tipo si es un tipo de valor o

• Primera invocación de cualquier constructor para ese tipo.

Dado que ninguna de esas condiciones está satisfecha, el constructor estático es no invocado. Las únicas piezas difíciles de observar son que "_x" es un campo de instancia, no un campo estático, y la construcción de una matriz de estructuras lo hace invocar en ningún constructores de instancia en los elementos de la matriz.

Otra muestra interesante:

   struct S
    {
        public int x;
        static S()
        {
            Console.WriteLine("static S()");
        }
        public void f() { }
    }

    static void Main() { new S().f(); }

Actualización: Mi observación es que, a menos que se use el estado estático, el constructor estático nunca se conmovió, algo que el tiempo de ejecución parece decidir y no se aplica a los tipos de referencia. Esto plantea la pregunta si es un error que queda porque tiene poco impacto, es por diseño, o es un error pendiente.

Actualización 2: Personalmente, a menos que esté haciendo algo funky en el constructor, este comportamiento del tiempo de ejecución nunca debe causar un problema. Tan pronto como accede al estado estático, se comporta correctamente.

update3: Más a un comentario de Lukeh, y haciendo referencia a la respuesta de Matthew Flaschen, la implementación y el llamamiento de su propio constructor en la estructura también desencadena el constructor estático que se debe llamar. Lo que significa que en uno de los tres escenarios, el comportamiento no es lo que dice en la lata.

Acabo de agregar una propiedad estática al tipo y accedí a esa propiedad estática, llamada el constructor estático. Sin el acceso de la propiedad estática, simplemente creando una nueva instancia del tipo, el constructor estático no se llamó.

internal struct SomeValType
    {
        public static int foo = 0;
        public int bar;

        static SomeValType()
        {
            Console.WriteLine("This never gets displayed");
        }
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // Doesn't hit static constructor
            SomeValType v = new SomeValType();
            v.bar = 1;

            // Hits static constructor
            SomeValType.foo = 3;
        }
    }

Una nota en este enlace Especifica que los constructores estáticos son no se llama cuando simplemente accede a las instancias:

http://www.jaggersoft.com/pubs/structsvsclasses.htm#default

Supongo que está creando una matriz de su tipo de valor.Así que la nueva palabra clave se usaría para inicializar la memoria para la matriz.

es válido para decir

SomeValType i;
i._x = 5;

sin palabra clave nueva en cualquier lugar, que es esencialmente lo que está haciendo aquí.Fueron un tipo de referencia un tipo de referencia, tendría que inicializar cada elemento de su matriz con

array[i] = new SomeRefType();

Este es un comportamiento de locaje por diseño del atributo "antes del campo" en MSIL. Afecta a C ++ / CLI también, presenté un informe de errores donde Microsoft explicó muy bien por qué el comportamiento es la forma en que está y señalé múltiples secciones en el estándar de idioma que no estaba de acuerdo / debe actualizarse para describir el comportamiento real . Pero no es públicamente visible. De todos modos, aquí está la última palabra de Microsoft (discutiendo una situación similar en C ++ / CLI):

ya que estamos invocando el estándar Aquí, la línea de la partición I, 8.9.5 dice esto:

si está marcado antes del campo, entonces el El método de inicializador de tipo se ejecuta en, o en algún momento antes, primer acceso a cualquier campo estático definido para eso tipo.

que la sección en realidad entra en detalle sobre cómo una implementación de idioma Puede optar por prevenir el comportamiento. estas describiendo C ++ / CLI elige no Para, más bien, lo permiten al programador. para hacerlo si lo desean.

básicamente, ya que el código a continuación tiene absolutamente ningún campo estático, el jit es completamente correcto en simplemente no invocando constructores de clase estática.

El mismo comportamiento es lo que está viendo, aunque en un idioma diferente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top