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 que quaisquer membros estáticos sejam referenciados. Um construtor estático será 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:

  1. Os campos estáticos são definidos como 0. Normalmente, isso é feito pelo runtime.
  2. Inicializadores de campo estático são executados. Os inicializadores de campo estático na execução de tipo mais derivada.
  3. 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.
  4. Construtores estáticos base são executados. Todos os construtores estáticos, começando por Object.Object cada classe base para a classe base direta.
  5. O construtor estático é executado. O construtor estático para o tipo é executado.

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.
  • Os construtores estáticos não podem ser herdados ou sobrecarregados.
  • Um construtor estático não pode ser chamado diretamente e destina-se apenas a ser chamado pela Common Language Runtime (CLR). 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 inicializadores de variável de campo estático estiverem presentes na classe do construtor estático, eles serão executados na ordem textual em que aparecem na declaração de classe. Os inicializadores são executados imediatamente antes da execução 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 o 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 são definidos explicitamente no código-fonte, a solução de problemas pode exigir inspeção do código de linguagem intermediária (IL).
  • 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 adicional é 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.
  • Os 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 pode ser verificado em 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#.

Confira também