¿Cómo funciona un constructor estático?

82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

Aquí está la secuencia que asumí

  1. Inicio del constructor estático
  2. Fin del constructor estático
  3. Inicio de principal
  4. Inicio de MyMethod
  5. Fin de principal

Ahora, en cualquier escenario, si 4 comenzará antes de 2, estoy jodido. ¿Es posible?

om471987
fuente
8
¿Es esta una pregunta de Java o C #? Ha puesto ambas etiquetas y no creo que la especificación sea la misma en los dos idiomas.
ARRG
En mi apertura, este es el mismo trabajo para ambos ... Pero soy el chico de C # ... Sry por eso
om471987
4
Java no tiene un constructor estático de la misma manera, solo bloques estáticos para la inicialización estática. static {// haz algo ...}
deraj
2
Personalmente, me siento incómodo con cualquier forma de interactividad dentro de un constructor estático. Entiendo su objetivo (hacer que cada método en esta clase estática espere hasta que el usuario esté autorizado antes de permitir que se ejecute), pero realmente no me gusta este método para lograrlo.
Brian
@Brian: - Sí ... Tienes razón ... solo estaba haciendo análisis ... Finalmente decidí no usar el constructor sino el método Initialize
om471987

Respuestas:

220

Aquí solo hizo una pregunta, pero hay una docena de preguntas que debería haber hecho, así que las responderé todas.

Aquí está la secuencia que asumí

  1. Inicio del constructor de clase (también conocido como cctor)
  2. Fin de cctor
  3. inicio de Main
  4. inicio de MyMethod

¿Es esto correcto?

No. La secuencia correcta es:

  1. Inicio del cctor para el programa, si lo hay. No hay.
  2. Fin de cctor para el programa, si hay uno. No hay.
  3. Inicio de Main
  4. Inicio de cctor para MyClass
  5. Fin de cctor para MyClass
  6. Inicio de MyClass.MyMethod

¿Qué pasa si hay un inicializador de campo estático?

El CLR puede cambiar el orden en el que se ejecutan los inicializadores de campo estático en algunos casos. Vea la página de Jon sobre el tema para más detalles:

Las diferencias entre constructores estáticos e inicializadores de tipo

¿Es posible alguna vez MyMethodllamar a un método estático como antes de que se complete el cctor de esa clase?

Si. Si el propio cctor llama a MyMethod, obviamente se llamará a MyMethod antes de que se complete el cctor.

El cctor no llama a MyMethod. ¿Es posible MyMethodque se llame a un método estático como antes de que se complete el cctor de MyClass?

Si. Si el cctor usa otro tipo cuyo cctor llama a MyMethod, entonces se llamará a MyMethod antes de que se complete el cctor MyClass.

¡Ningún actor llama a MyMethod, directa o indirectamente! Ahora bien, ¿alguna vez es posible MyMethodque se llame a un método estático como antes de que se complete el cctor de MyClass?

No.

¿Sigue siendo cierto incluso si hay varios hilos involucrados?

Si. El cctor terminará en un hilo antes de que se pueda llamar al método estático en cualquier hilo.

¿Se puede llamar al cctor más de una vez? Suponga que dos subprocesos hacen que se ejecute el cctor.

Se garantiza que el cctor se llamará como máximo una vez, sin importar cuántos subprocesos estén involucrados. Si dos hilos llaman a MyMethod "al mismo tiempo", corren. Uno de ellos pierde la carrera y bloquea hasta que el cctor MyClass completa el hilo ganador.

¿El hilo perdedor se bloquea hasta que termina el ciclo? ¿ De verdad ?

De Verdad.

Entonces, ¿qué pasa si el cctor en el hilo ganador llama al código que bloquea en un bloqueo tomado previamente por el hilo perdedor ?

Entonces tienes una condición clásica de inversión de orden de bloqueo. Su programa se bloquea. Siempre.

Eso parece peligroso. ¿Cómo puedo evitar el estancamiento?

Si te duele cuando haces eso, deja de hacerlo . Nunca hagas algo que pueda bloquear un cctor.

¿Es una buena idea confiar en la semántica de inicialización de cctor para hacer cumplir los requisitos de seguridad complejos? ¿Y es una buena idea tener un actor que interactúe con los usuarios?

Tampoco lo son las buenas ideas. Mi consejo es que debe encontrar una manera diferente de asegurarse de que se cumplan las condiciones previas de sus métodos que afectan la seguridad.

Eric Lippert
fuente
5
Eric, tengo curiosidad por saber por qué reemplazó "constructor estático" con "constructor de clase" o "cctor" en esta respuesta. ¿Es incorrecto usar "constructor estático" cuando se hace referencia a un cctor?
phoog
6
@phoog: Quería ser coherente en mi uso de la terminología, así que elegí la más corta. Tanto el "constructor estático" como el "constructor de clases" están bien. Como detalle de implementación, el constructor estático de un tipo se emite como un método especial llamado ".cctor", por lo que es común referirse a dicho constructor como "un cctor". Si estuviera escribiendo en un contexto más formal, usaría uno de los términos más largos.
Eric Lippert
@EricLippert ¿Esto también es cierto para las clases no estáticas con un constructor estático?
Leyendas
2
@Legends: ¿Esto también es cierto para las clases no estáticas con un constructor estático? Si.
Eric Lippert
24

Según MSDN , un constructor estático:

Se llama automáticamente a un constructor estático para inicializar la clase antes de que se cree la primera instancia o se haga referencia a cualquier miembro estático.

Por lo tanto, se llamará al constructor estático antes de que MyClass.MyMethod()se invoque el método estático (suponiendo que no se invoca también durante la construcción estática o la inicialización del campo estático, por supuesto).

Ahora, si está haciendo algo asincrónico en eso static constructor, entonces es su trabajo sincronizar eso.

James Michael Hare
fuente
7
Si está haciendo algo asincrónico que involucre un segundo hilo en un constructor estático, se encontrará con un mundo de dolor . Nada acelera los puntos muertos. Consulte stackoverflow.com/a/8883117/88656 para ver un ejemplo.
Eric Lippert
@Eric: de acuerdo ... no me gustaría hacer eso, pero no estaba segura de formar su ejemplo qué es exactamente lo que le faltaba para ser terminado en el momento en que se llamó MiMetodo ...
James Michael Hare
11

El n. ° 3 es en realidad el n. ° 1: la inicialización estática no comienza hasta el primer uso de la clase a la que pertenece.

Es posible si MyMethodse llama desde el constructor estático o desde un bloque de inicialización estático. Si no invoca MyMethoddirecta o indirectamente desde su constructor estático, debería estar bien.

Sergey Kalinichenko
fuente
Solo como una nota, tengo entendido que la staticinicialización se puede llamar antes del primer uso dependiendo de la elegibilidad para las optimizaciones.
James Michael Hare
1
Para un constructor estático, cierto, pero para la inicialización estática era mi punto. Lo siento, tal vez solo estaba recogiendo nits en la frase 'la inicialización estática no comienza ...' eso es cierto para la construcción estática, pero no si la clase no tiene un constructor estático, la inicialización estática puede ocurrir antes.
James Michael Hare
Lo siento, probablemente estoy analizando demasiado la verborrea. En el contexto de la pregunta, es absolutamente correcto, solo me preocupaba esa oración como una declaración independiente para la inicialización estática en el contexto de clases sin constructores estáticos explícitos.
James Michael Hare
@James: No estás analizando demasiado, la terminología es la diferencia crucial aquí. Los constructores estáticos son un concepto de C #, mientras que la inicialización de tipos es algo de .NET. El código dentro de un constructor estático (C #) se convierte en parte del inicializador de tipo (.NET), pero cuándo y cómo se activa el inicializador de tipo (es decir, la beforefieldinitsemántica) está determinado por si la clase C # tiene o no un constructor estático.
LukeH
9

De la documentación (el énfasis es mío):

Se llama automáticamente a un constructor estático para inicializar la clase antes de que se cree la primera instancia o se haga referencia a cualquier miembro estático .

conocido
fuente
2

Puede garantizar que 4 siempre vendrá después de 2 (si no crea una instancia de su clase a partir de su método estático), sin embargo, no ocurre lo mismo con 1 y 3.

Scott Chamberlain
fuente
2

Se llamará al constructor estático antes de que se ejecute mymethod. Sin embargo, si está jodido si se llama 4 antes que 2, le sugiero que reconsidere su diseño. De todos modos, no debería estar haciendo cosas complicadas en un constructor estático.

Umair
fuente
2

CLR garantiza que el constructor estático se ejecuta antes de que se acceda a cualquier miembro estático. Sin embargo, tu diseño huele un poco mal. Sería más sencillo hacer algo como esto:

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

Con su diseño, si la autenticación falla, la única forma de evitar que MyMethod se ejecute es lanzando una excepción.

phoog
fuente
2

Se garantiza que se haya llamado al constructor de una clase estática antes de que se ejecute cualquiera de sus métodos. Ejemplo:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

Salida:

Presione enter

// después de presionar enter

Hola entrante ...

¡Hola!

¡Hola!

haiyyu
fuente
usando el sistema; espacio de nombres MyNameSpace {class Program {static void Main (string [] args) {Console.WriteLine ("Ingresado en main"); Boop.SayHi (); Boop.SayHi (); }} clase estática Boop {static Boop () {Console.Read (); Console.WriteLine ("Clave de constructor ingresada"); } public static void SayHi () {Console.WriteLine ("Se llama al método"); }}} sí, este programa ofrece una mejor comprensión
om471987
Posiblemente. Sin embargo, la próxima vez publíquelo como respuesta. Entonces es más útil y visible.
haiyyu
1

Aquí está el orden real en el que van las cosas:

  1. Inicio de Main
  2. Inicio del MyClassconstructor estático
  3. Fin del MyClassconstructor estático
  4. Inicio de MyMethod
  5. Final de Main

fuente
0

O puede pasar por el depurador.

Saille
fuente