Construtores estáticos (Guia de Programação em C#)
Um construtor estático é usado para inicializar quaisquer dados estáticos ou para executar uma ação específica que precisa ser executada apenas uma vez. Ele é chamado automaticamente antes que a primeira instância seja criada ou os membros estáticos sejam referenciados. Um construtor estático é chamado no máximo uma vez.
class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;
// Static constructor is called at most one time, before any
// instance constructor is invoked or member is accessed.
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}
Há várias ações que fazem parte da inicialização estática. Essas ações ocorrem na seguinte ordem:
- Os campos estáticos são definidos como 0. O runtime normalmente faz essa inicialização.
- Inicializadores de campo estático são executados. Os inicializadores de campo estático na execução de tipo mais derivada.
- Inicializadores de campo estático do tipo base são executados. Inicializadores de campo estático começando com a base direta por meio de cada tipo base para System.Object.
- Qualquer construtor estático é executado. Todos os construtores estáticos, desde a classe base final de Object.Object até cada classe base até a execução de tipo. A ordem de execução do construtor estático não é especificada. No entanto, todos os construtores estáticos na hierarquia são executados antes de qualquer instância ser criada.
Importante
Há uma exceção importante à regra em que um construtor estático é executado antes de qualquer instância ser criada. Se um inicializador de campo estático criar uma instância do tipo, esse inicializador será executado (incluindo qualquer chamada para um construtor de instância) antes da execução do construtor estático. Isso é mais comum no padrão singleton, conforme mostrado no exemplo a seguir:
public class Singleton
{
// Static field initializer calls instance constructor.
private static Singleton instance = new Singleton();
private Singleton()
{
Console.WriteLine("Executes before static constructor.");
}
static Singleton()
{
Console.WriteLine("Executes after instance constructor.");
}
public static Singleton Instance => instance;
}
Um inicializador de módulo pode ser uma alternativa a um construtor estático. Para obter mais informações, consulte a especificação para inicializadores de módulo.
Comentários
Construtores estáticos têm as seguintes propriedades:
- Um construtor estático não usa modificadores de acesso nem tem parâmetros.
- Uma classe ou struct só pode ter um construtor estático.
- Construtores estáticos não podem ser herdados ou sobrecarregados.
- Um construtor estático não pode ser chamado diretamente e deve ser chamado apenas pelo CLR (Common Language Runtime). Ele é invocado automaticamente.
- O usuário não tem controle sobre quando o construtor estático é executado no programa.
- Um construtor estático é chamado automaticamente. Ele inicializa a classe antes de a primeira instância ser criada ou de os membros estáticos declarados nessa classe (não nas classes base) serem referenciados. Um construtor estático será executado antes de um construtor de instância. Se os inicializadores de variável de campo estático estiverem presentes na classe do construtor estático, eles serão executados na ordem textual na qual aparecem na declaração de classe. Os inicializadores são executados imediatamente antes do construtor estático.
- Se você não fornecer um construtor estático para inicializar campos estáticos, todos os campos estáticos serão inicializados com seu valor padrão, conforme listado nos Valores padrão de tipos C#.
- Se um construtor estático gerar uma exceção, o runtime não a invocará uma segunda vez e o tipo permanecerá não inicializado durante o tempo de vida do domínio do aplicativo. Normalmente, uma exceção TypeInitializationException é lançada quando um construtor estático não consegue instanciar um tipo ou uma exceção sem tratamento que ocorre em um construtor estático. Para construtores estáticos que não estão explicitamente definidos no código-fonte, a solução de problemas pode exigir a inspeção do código IL (linguagem intermediária).
- A presença de um construtor estático impede a adição do atributo do tipo BeforeFieldInit. Isso limita a otimização do runtime.
- Um campo declarado como
static readonly
só pode ser atribuído como parte de sua declaração ou em um construtor estático. Quando um construtor estático explícito não for necessário, inicialize os campos estáticos na declaração, em vez de usar um construtor estático para melhorar a otimização do runtime. - O runtime chama um construtor estático não mais do que uma vez em um único domínio do aplicativo. Essa chamada é feita em uma região bloqueada com base no tipo específico da classe. Nenhum mecanismo de bloqueio extra é necessário no corpo de um construtor estático. Para evitar o risco de deadlocks, não bloqueie o thread atual em inicializadores e construtores estáticos. Por exemplo, não aguarde tarefas, threads, identificadores de espera ou eventos, não adquira bloqueios e não execute o bloqueio de operações paralelas, como loops paralelos
Parallel.Invoke
e consultas LINQ paralelas.
Observação
Embora não seja diretamente acessível, a presença de um construtor estático explícito deve ser documentada para auxiliar na solução de problemas de exceções de inicialização.
Uso
- Um uso típico de construtores estáticos é quando a classe está usando um arquivo de log e o construtor é usado para gravar entradas nesse arquivo.
- Construtores estáticos também são úteis ao criar classes wrapper para código não gerenciado quando o construtor pode chamar o método
LoadLibrary
. - Construtores estáticos também são um local conveniente para impor verificações em tempo de execução no parâmetro de tipo que não podem ser verificadas no tempo de compilação por meio de restrições de parâmetro de tipo.
Exemplo
Nesse exemplo, a classe Bus
tem um construtor estático. Quando a primeira instância do Bus
for criada (bus1
), o construtor estático será invocado para inicializar a classe. O exemplo de saída verifica se o construtor estático é executado somente uma vez, mesmo se duas instâncias de Bus
forem criadas e se é executado antes que o construtor da instância seja executado.
public class Bus
{
// Static variable used by all Bus instances.
// Represents the time the first bus of the day starts its route.
protected static readonly DateTime globalStartTime;
// Property for the number of each bus.
protected int RouteNumber { get; set; }
// Static constructor to initialize the static variable.
// It is invoked before the first instance constructor is run.
static Bus()
{
globalStartTime = DateTime.Now;
// The following statement produces the first line of output,
// and the line occurs only once.
Console.WriteLine("Static constructor sets global start time to {0}",
globalStartTime.ToLongTimeString());
}
// Instance constructor.
public Bus(int routeNum)
{
RouteNumber = routeNum;
Console.WriteLine("Bus #{0} is created.", RouteNumber);
}
// Instance method.
public void Drive()
{
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
// For demonstration purposes we treat milliseconds as minutes to simulate
// actual bus times. Do not do this in your actual bus schedule program!
Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
this.RouteNumber,
elapsedTime.Milliseconds,
globalStartTime.ToShortTimeString());
}
}
class TestBus
{
static void Main()
{
// The creation of this instance activates the static constructor.
Bus bus1 = new Bus(71);
// Create a second bus.
Bus bus2 = new Bus(72);
// Send bus1 on its way.
bus1.Drive();
// Wait for bus2 to warm up.
System.Threading.Thread.Sleep(25);
// Send bus2 on its way.
bus2.Drive();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Sample output:
Static constructor sets global start time to 3:57:08 PM.
Bus #71 is created.
Bus #72 is created.
71 is starting its route 6.00 minutes after global start time 3:57 PM.
72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/
Especificação da linguagem C#
Para saber mais, confira a seção Construtores estáticos da Especificação da linguagem C#.