Hay varias dificultades para los constructores estáticos. Por ejemplo, si un constructor estático throws an exception, continuaría recibiendo un TypeInitializationException
cada vez que acceda a cualquiera de sus miembros.
Si un constructor estático produce una excepción, el tiempo de ejecución no invocará una segunda vez, y el tipo se mantendrá sin inicializar durante la vida útil del dominio de aplicación en el que el programa se está ejecutando.
En general, las clases estáticas solo deben usarse en escenarios sin estado donde no necesitará ninguna inicialización. Si la clase tiene que ser inicializado, puede ser mejor usar el singleton pattern, que puede ser lazily initialized en el primer acceso:
public class MyClass
{
private static readonly Lazy<MyClass> current =
new Lazy<MyClass>(() => new MyClass());
public static MyClass Current
{
get { return current.Value; }
}
private MyClass()
{
// Initialization goes here.
}
public void Foo()
{
// ...
}
public void Bar()
{
// ...
}
}
static void Main(string[] args)
{
MyClass.Current.Foo(); // Initialization only performed here.
MyClass.Current.Bar();
MyClass.Current.Foo();
}
Editar: Hice una cierta lectura más arriba en la materia, y parece que los constructores estáticos do causan interbloqueos si realiza operaciones de bloqueo (por ejemplo, devoluciones de llamada asincrónicas o sincronización de subprocesos) dentro de ellos.
El CLR utiliza internamente el bloqueo para evitar que los inicializadores de tipo (constructores estáticos) se ejecuten varias veces al mismo tiempo. Por lo tanto, si su constructor estático intenta acceder a otro miembro de su tipo declarante desde otro hilo, inevitablemente se estancará. Como "otro miembro" podría ser una función anónima declarada como parte de una operación PLINQ o TPL, estos errores pueden ser sutiles y difíciles de identificar.
Igor Ostrovsky (MSFT) explica esto en su Static constructor deadlocks artículo, proporcionando el siguiente ejemplo de un callejón sin salida:
using System.Threading;
class MyClass
{
static void Main() { /* Won’t run... the static constructor deadlocks */ }
static MyClass()
{
Thread thread = new Thread(arg => { });
thread.Start();
thread.Join();
}
}
En el ejemplo anterior, el nuevo hilo necesita tener acceso a la función anónima vacía, { }
, define como su devolución de llamada. Sin embargo, dado que la función anónima se compila como otro método privado de MyClass
entre bastidores, el nuevo subproceso no puede acceder antes de que se inicialice el tipo MyClass
. Y, dado que el constructor estático MyClass
necesita esperar a que el nuevo subproceso se complete primero (debido a thread.Join()
), se produce un interbloqueo.
¿Hay algo más que excepciones arrojadas dentro del constructor? Por ejemplo, ¿qué podría explicar el escenario de "punto muerto" que estoy experimentando? ¿Hay algún bloqueo relacionado con tipos estáticos de alguna manera detrás de escena? –
@liortal: Respondido arriba. – Douglas
¿Hay alguna diferencia entre lo que tienes en el primer ejemplo y estas líneas en su lugar? 'private static readonly Lazy current; static MyClass {current = new Lazy ((= = new new MyClass()); } ' (lo siento, parece que no puede obtener el formato correcto :)) –
Mark