Todo tipo puede tener opcionalmente
un constructor de tipo, que es un
método especial que funciona de forma similar a los constructores ordinarios
sólo que para lo que se usa es para inicializar los campos static del tipo donde
se ha definido.
Cada tipo de dato sólo puede
tener un constructor de tipo. Éste constructor es llamado automáticamente por
el compilador la primera vez que se accede al tipo, ya sea para crear objetos
del mismo o para acceder a sus campos estáticos. Esta llamada se hace justo
después de inicializar los campos estáticos del tipo con los valores iniciales
especificados al definirlos (o, en su ausencia, con los valores por defecto de
sus tipos de dato), por lo que el programador no tiene forma de controlar la
forma en que se le llama y, por tanto, no puede pasarle parámetros que
condicionen su ejecución.
Como cada tipo sólo puede tener
un constructor de tipo no tiene sentido poderse usar this en su
inicializador para llamar a otro. Y además, tampoco tiene sentido usar base
debido a que éste siempre hará referencia al constructor de tipo sin parámetros
de su clase base. O sea, un constructor de tipo no puede tener
inicializador.
Además, no tiene sentido darle
modificadores de acceso ya que el programador nunca lo podrá llamar sino que
sólo será llamado automáticamente y sólo al accederse al tipo por primera vez.
Como es absurdo, el compilador considera un error dárselos.
La forma en que se define el
constructor de tipo es similar a la de los constructores normales, sólo que
ahora la definición ha de ir prefijada del modificador static y no puede
contar con parámetros ni inicializador. O sea, se define de la siguiente
manera:
static
<nombreTipo>()
{
<código>
}
En la especificicación de C# no
se ha recogido cuál ha de ser el orden exacto de las llamadas a los constructores
de tipos cuando se combinan con herencia, aunque lo que sí se indica es que se
ha de asegurar de que no se accede a un campo estático sin haberse ejecutado
antes su constructor de tipo. Todo esto puede verse más claro con un ejemplo:
using System;
class A
{
public static X;
static A()
{
Console.WriteLine(“Constructor
de A”);
X=1;
}
}
class B:A
{
static B()
{
Console.WriteLine(“Constructor
de B”);
X=2;
}
public static void
Main()
{
B b = new
B();
Console.WriteLine(B.X);
}
}
La salida que muestra por
pantalla la ejecución de este programa es la siguiente:
Inicializada clase
B
Inicializada clase
A
2
En principio la salida de este
programa puede resultar confusa debido a que los primeros dos mensajes parecen
dar la sensación de que la creación del objeto b
provocó que se ejecutase el constructor de la clase hija antes que al de la
clase padre, pero el último mensaje se corresponde con una ejecución en el
orden opuesto. Pues bien, lo que ha ocurrido es lo siguiente: como el orden de
llamada a constructores de tipo no está establecido, el compilador de Microsoft
ha llamado antes al de la clase hija y por ello el primer mensaje mostrado es Inicializada clase B. Sin
embargo, cuando en este constructor se va a acceder al campo X se detecta que la clase donde se definió aún no
está inicializada y entonces se llama a su constructor de tipo, lo que hace que
se muestre el mensaje Incializada
clase A. Tras esta llamada se machaca el valor que el constructor de A dió a X
(valor 1) por el valor que el constructor de B
le da (valor 2) Finalmente, el último WriteLine()
muestra un 2, que es el
último valor escrito en X.